Python 入門:裝飾器(decorator)、@functools.wraps、@staticmethod、@classmethod
裝飾器
1、要了解裝飾器,就需要知道什麼是高階函式,高階函式就是將函式作為引數賦值給另一個函式
2、Python的 decorator 本質上就是一個高階函式,它接收一個函式作為引數,然後,返回一個新函式
3、decorator是在被裝飾的函式前加@函式名的函式來修飾下面的函式
#被裝飾函式
def now():
print(‘2015-3-3’)
想要對被裝飾函式新增(修飾)什麼功能,就可以寫一個特定的函式,然後在被裝飾的函式前加@函式名
#需要裝飾其它函式時的新功能函式
def log(func):
def wrapper(*args,**kw):
print(‘call %s(): ’ % func.__name__)
return func(*args,**kw)
return wrapper
完成裝飾
@log
def now():
return print(‘2015-3-3’)
呼叫now()函式,不僅會執行now()函式本身,還會在執行now()函式前列印一行日誌:
>>> now()
call now():
2015-3-3
把@log放到now()函式的定義處,相當於執行了語句:
now = log(now)
由於log()是一個decorator,返回一個函式,所以,原來的now()函式仍然存在,只是現在同名的now變數指向了新的函式,於是呼叫now()將執行新函式,即在log()函式中返回的wrapper()函式。
++++++++++++++++++++++++++++++++++++++
帶引數的裝飾器
def log(text):
def decorator(func):
def wrapper(*args,**kw):
print(‘%s %s():’ % (text,fun.__name__))
return func(*args,**kw)
return wrapper
return decorator
這個3層巢狀的decorator用法如下:
@log('execute')
def now():
print('2015-3-25')
執行結果如下:
>>> now ()
execute now():
2015-3-25
和兩層巢狀的decorator相比,3層巢狀的效果是這樣的:
>>> now = log('execute')(now)
我們來剖析上面的語句,首先執行log(‘execute’),返回的是decorator函式,再呼叫返回的函式,引數是now函式,返回值最終是wrapper函式。
以上兩種decorator的定義都沒有問題,但還差最後一步。因為我們講了函式也是物件,它有name等屬性,但你去看經過decorator裝飾之後的函式,它們的name已經從原來的’now’變成了’wrapper’:
>>> now.__name__
'wrapper'
因為返回的那個wrapper()函式名字就是’wrapper’,所以,需要把原始函式的name等屬性複製到wrapper()函式中,否則,有些依賴函式簽名的程式碼執行就會出錯。
不需要編寫wrapper.name = func.name這樣的程式碼,Python內建的functools.wraps就是幹這個事的,所以,一個完整的decorator的寫法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
或者針對帶引數的decorator:
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
import functools是匯入functools模組。模組的概念稍候講解。現在,只需記住在定義wrapper()的前面加上@functools.wraps(func)即可。
類裝飾器
#mylocker.py
class mylocker:
def __init__(self):
print("mylocker.__init__() called.")
@staticmethod
def acquire():
print("mylocker.acquire() called.")
@staticmethod
def unlock():
print(" mylocker.unlock() called.")
class lockerex(mylocker):
@staticmethod
def acquire():
print("lockerex.acquire() called.")
@staticmethod
def unlock():
print(" lockerex.unlock() called.")
def lockhelper(cls):
'''cls 必須實現acquire和release靜態方法'''
def _deco(func):
def __deco(*args, **kwargs):
print("before %s called." % func.__name__)
cls.acquire() #這裡需要不建立例項呼叫類的方法,所以需要在acquire方法的上方使用@staticmethod
try:
return func(*args, **kwargs)
finally:
cls.unlock() #這裡需要不建立例項呼叫類的方法,所以需要在unlock方法的上方使用@staticmethod
return __deco
return _deco
#decorate.py
from mylocker import *
class example:
@lockhelper(mylocker)
def myfunc(self):
print(" myfunc() called.")
@lockhelper(mylocker)
@lockhelper(lockerex)
def myfunc2(self, a, b):
print(" myfunc2() called.")
return a + b
if __name__=="__main__":
a = example()
a.myfunc()
print(a.myfunc())
print(a.myfunc2(1, 2))
print(a.myfunc2(3, 4))
@staticmethod與@classmethod的區別
一般來說,要使用某個類的方法,需要先例項化一個物件再呼叫方法。
而使用@staticmethod或@classmethod,就可以不需要例項化,直接類名.方法名()來呼叫。
這有利於組織程式碼,把某些應該屬於某個類的函式給放到那個類裡去,同時有利於名稱空間的整潔。
既然@staticmethod和@classmethod都可以直接類名.方法名()來呼叫,那他們有什麼區別呢
從它們的使用上來看,
@staticmethod不需要表示自身物件的self和自身類的cls引數,就跟使用函式一樣。
@classmethod也不需要self引數,但第一個引數需要是表示自身類的cls引數。
如果在@staticmethod中要呼叫到這個類的一些屬性方法,只能直接類名.屬性名或類名.方法名。
而@classmethod因為持有cls引數,可以來呼叫類的屬性,類的方法,例項化物件等,避免硬編碼。
下面上程式碼。
[python] view plain copy
class A(object):
bar = 1
def foo(self):
print 'foo'
@staticmethod
def static_foo():
print 'static_foo'
print A.bar
@classmethod
def class_foo(cls): #這裡用了cls引數,即A這個類本身,後面要使用類.屬性或類.方法時就可以用cls.屬性或cls.方法,避免硬編碼
print 'class_foo'
print cls.bar
cls().foo() #類.方法的呼叫,沒有使用類的名字(A),避免硬編碼
A.static_foo()
A.class_foo()
輸出
static_foo
1
class_foo
1
foo