翻譯:《實用的Python編程》07_05_Decorated_methods
目錄 | 上一節 (7.4 裝飾器) | 下一節 (8 測試和調試)
7.5 裝飾方法
本節討論一些與方法定義結合使用的內置裝飾器。
預定義的裝飾器
在類定義中,有許多預定義的裝飾器用于指定特殊類型的方法。
class Foo:
def bar(self,a):
...
@staticmethod
def spam(a):
...
@classmethod
def grok(cls,a):
...
@property
def name(self):
...
讓我們逐個查看吧。
靜態方法
@staticmethod 用于定義所謂的靜態類方法( static class method,來自于 C++/Java)。靜態方法是一個函數,這個函數是類的一部分,但不是在實例上進行操作。
class Foo(object):
@staticmethod
def bar(x):
print('x =', x)
>>> Foo.bar(2)
x=2
>>>
靜態方法有時用于實現類的內部支持代碼,例如,用于幫助管理已創建的實例(內存管理,系統資源,持久化,鎖等等)。有時也用于某些設計模式(這里暫不討論)。
類方法
@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.py 和 portfolio.py 文件中, Portfolio 類的創建稍微有點混亂。例如,report.py 程序具有如下代碼:
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 的代碼以使用類方法。
浙公網安備 33010602011771號