翻译:《实用的Python编程》07_05_Decorated_methods

目录 | 上一节 (7.4 装饰器) | 下一节 (8 测试和调试)python

7.5 装饰方法

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

预约义的装饰器

在类定义中,有许多预约义的装饰器用于指定特殊类型的方法。github

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

    @staticmethod
    def spam(a):
        ...

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

    @property
    def name(self):
        ...

让咱们逐个查看吧。segmentfault

静态方法

@staticmethod 用于定义所谓的静态类方法( static class method,来自于 C++/Java)。静态方法是一个函数,这个函数是类的一部分,但不是在实例上进行操做。设计模式

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

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

静态方法有时用于实现类的内部支持代码,例如,用于帮助管理已建立的实例(内存管理,系统资源,持久化,锁等等)。有时也用于某些设计模式(这里暂不讨论)。app

类方法

@classmethod 用于定义类方法(class methods)。类方法是一种将 对象而不是实例做为第一个参数的方法。函数

class Foo:
    def bar(self):
        print(self)

    @classmethod
    def spam(cls):
        print(cls)

>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690>   # The instance `f`
>>> Foo.spam()
<class '__main__.Foo'>              # The class `Foo`
>>>

类方法经常使用做定义替代构造函数(constructor)的工具。工具

import time
class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def today(cls):
        # Notice how the class is passed as an argument
        tm = time.localtime()
        # And used to create a new instance
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

d = Date.today()

类方法能够和继承等特性一块儿使用以解决棘手的问题。测试

class Date:
    ...
    @classmethod
    def today(cls):
        # Gets the correct class (e.g. `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 程序具备以下代码:spa

def read_portfolio(filename, **opts):
    '''
    Read a stock portfolio file into a list of dictionaries with keys
    name, shares, and 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('Data/portfolio.csv') as lines:
...     port = Portfolio.from_csv(lines)
...
>>>

请对 Portfolio 类进行修改,而后修改 report.py 的代码以使用类方法。

目录 | 上一节 (7.4 装饰器) | 下一节 (8 测试和调试)

注:完整翻译见 https://github.com/codists/practical-python-zh