内置方法装饰器介绍

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

本节讨论一些与方法定义结合使用的内置装饰器。

预定义装饰器

在类定义中,有一些预定义的装饰器用于指定特殊类型的方法。

class Foo:
    def bar(self,a):
     ...

    @staticmethod
    def spam(a):
     ...

    @classmethod
    def grok(cls,a):
     ...

    @property
    def name(self):
     ...

让我们逐个来看。

静态方法

@staticmethod 用于定义所谓的 静态 类方法(源自 C++/Java)。静态方法是类的一部分,但它 作用于实例。

class Foo(object):
    @staticmethod
    def bar(x):
        print('x =', x)

>>> Foo.bar(2) ## x=2
>>>

静态方法有时用于为类实现内部支持代码。例如,用于帮助管理已创建实例的代码(内存管理、系统资源、持久性、锁定等)。某些设计模式也会使用它们(此处不讨论)。

类方法

@classmethod 用于定义类方法。类方法是一种将 对象作为第一个参数而非实例接收的方法。

class Foo:
    def bar(self):
        print(self)
    @classmethod
    def spam(cls):
        print(cls)

>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690>   ## 实例 `f`
>>> Foo.spam()
<class '__main__.Foo'>              ## 类 `Foo`
>>>

类方法最常被用作定义备用构造函数的工具。

class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):
        ## 注意类是如何作为参数传递的
        tm = time.localtime()
        ## 并用于创建一个新实例
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

d = Date.today()

类方法解决了一些与继承等特性相关的棘手问题。

class Date:
  ...
    @classmethod
    def today(cls):
        ## 获取正确的类(例如 `NewDate`)
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

class NewDate(Date):
  ...

d = NewDate.today()

练习 7.11:类方法的实际应用

在你的 report.pyportfolio.py 文件中,创建一个 Portfolio 对象的过程有点混乱。例如,report.py 程序有这样的代码:

def read_portfolio(filename, **opts):
    '''
    将股票投资组合文件读入一个字典列表,字典的键为
    name、shares 和 price。
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

    portfolio = [ Stock(**d) for d in portdicts ]
    return Portfolio(portfolio)

portfolio.py 文件定义 Portfolio() 时使用了一个奇怪的初始化器,如下所示:

class Portfolio:
    def __init__(self, holdings):
        self._holdings = holdings
  ...

坦率地说,责任链有点混乱,因为代码很分散。如果一个 Portfolio 类应该包含一个 Stock 实例列表,也许你应该把这个类改得更清晰一些。像这样:

## portfolio.py

import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)
  ...

如果你想从 CSV 文件中读取一个投资组合,也许你应该为它创建一个类方法:

## portfolio.py

import fileparse
import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)

    @classmethod
    def from_csv(cls, lines, **opts):
        self = cls()
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

        for d in portdicts:
            self.append(stock.Stock(**d))

        return self

要使用这个新的 Portfolio 类,你现在可以编写如下代码:

>>> from portfolio import Portfolio
>>> with open('portfolio.csv') as lines:
...     port = Portfolio.from_csv(lines)
...
>>>

Portfolio 类进行这些更改,并修改 report.py 代码以使用类方法。

✨ 查看解决方案并练习

总结

恭喜你!你已经完成了装饰方法实验。你可以在 LabEx 中练习更多实验来提升你的技能。