裝飾器是 Python 進階編程的核心概念,廣泛用於日誌、權限驗證、緩存、計時等橫切關注點。本文通過九個遞進示例,帶你徹底理解裝飾器原理與寫法。
def myfunc():
print("myfunc() called.")
myfunc()
myfunc()
def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
def myfunc():
print("myfunc() called.")
myfunc = deco(myfunc) # 手動裝飾
myfunc()
myfunc()
@def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
@deco
def myfunc():
print("myfunc() called.")
myfunc()
myfunc()
問題:裝飾邏輯只在導入時執行一次,原函數被多調用一次。
def deco(func):
def _deco():
print("before myfunc() called.")
func()
print("after myfunc() called.")
return _deco
@deco
def myfunc():
print("myfunc() called.")
return 'ok'
myfunc()
myfunc()
def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print("after myfunc() called. result:", ret)
return ret
return _deco
@deco
def myfunc(a, b):
print("myfunc(%s,%s) called." % (a, b))
return a + b
myfunc(1, 2)
myfunc(3, 4)
*args, **kwargs)def deco(func):
def _deco(*args, **kwargs):
print("before %s called." % func.__name__)
ret = func(*args, **kwargs)
print("after %s called. result: %s" % (func.__name__, ret))
return ret
return _deco
@deco
def myfunc(a, b):
return a + b
@deco
def myfunc2(a, b, c):
return a + b + c
def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print("after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print("myfunc() called.")
@deco("module2")
def myfunc2():
print("myfunc2() called.")
class Locker:
@staticmethod
def acquire(): print("locker.acquire()")
@staticmethod
def release(): print("locker.release()")
def deco(cls):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, cls))
cls.acquire()
try:
return func()
finally:
cls.release()
return __deco
return _deco
@deco(Locker)
def myfunc():
print("myfunc() called.")
myfunc()
myfunc()
# mylocker.py
class MyLocker:
@staticmethod
def acquire(): print("mylocker.acquire()")
@staticmethod
def unlock(): print("mylocker.unlock()")
class LockerEx(MyLocker):
@staticmethod
def acquire(): print("lockerex.acquire()")
@staticmethod
def unlock(): print("lockerex.unlock()")
def lockhelper(cls):
def _deco(func):
def __deco(*args, **kwargs):
print("before %s called." % func.__name__)
cls.acquire()
try:
return func(*args, **kwargs)
finally:
cls.unlock()
return __deco
return _deco
# 使用示例
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__":
e = Example()
e.myfunc()
print(e.myfunc2(1, 2))
裝飾器本質是 高階函數,接收函數返回函數。掌握「內嵌包裝函數保持簽名」「三層嵌套實現帶參裝飾器」「functools.wraps 保留元信息」三大核心技巧,即可靈活應對各種實戰場景。