翻譯:《實用的Python編程》07_05_Decorated_methods

目錄 | 上一節 (7.4 裝飾器) | 下一節 (8 測試和調試)html

7.5 裝飾方法

本節討論一些與方法定義結合使用的內置裝飾器。python

預約義的裝飾器

在類定義中,有許多預約義的裝飾器用於指定特殊類型的方法。git

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

    @staticmethod
    def spam(a):
        ...

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

    @property
    def name(self):
        ...

讓咱們逐個查看吧。github

靜態方法

@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

相關文章
相關標籤/搜索