最近很多人问我关于Python装饰器的问题,我把它总结成blog方便他人和自己。
借鉴了这篇关于装饰器的博文,http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html。同时加入了自己的补充和心得。
我用Python IDE来调试,用了Python3.4.2。输出用>>>表示。
第一步:编写一个最简单的函数
#!/usr/bin/env python
def myfunc():
print('myfunc() called')
myfunc()
>>>myfunc() called
第二步:使用装饰函数在函数执行前后分别附加额外功能
#!/usr/bin/env python
def deco(func):
print('before myfunc() called')
func()
print('after myfunc() called')
return func
def myfunc():
print('myfunc() called')
deco(myfunc)
>>>before myfunc() called
如果调用函数改为:
myfunc = deco(myfunc)
myfunc()
>>>before myfunc() called
如果调用函数改为:
myfunc = deco(myfunc)
>>>before myfunc() called
#!/usr/bin/env python
def deco(func):
print('before myfunc() called')
func()
print('after myfunc() called')
return func
@deco
def myfunc():
print('myfunc() called')
myfunc()
>>>before myfunc() called
如果改为:
myfunc
>>>before myfunc() called
第四步:使用内嵌包装函数来确保每次新函数都被调用
#!/usr/bin/env python
def deco(func):
def wrapper():
print('before myfunc() called')
func()
print('after myfunc() called')
return wrapper
@deco
def myfunc():
print('myfunc() called')
myfunc()
myfunc()相当于执行包装函数wrapper().
如果调用改为
myfunc
>>>
没有输出,因为这样只是得到了包装函数wrapper的地址,并没有真正去运行它。
第五步:对带固定参数的函数进行装饰
#!/usr/bin/env python
def deco(func):
def wrapper(a,b): ##wrapper的入参必须和func一致
print('before myfunc() called')
func(a,b)
print('after myfunc() called')
return wrapper
@deco
def myfunc(a,b):
ret = a+b
print('{}+{}={}'.format(a,b,ret))
print('myfunc() called')
myfunc(1,2)
#!/usr/bin/env python
def deco(func):
def wrapper(*args,**kwargs):
print('before myfunc() called')
func(*args,**kwargs)
print('after myfunc() called')
return wrapper
@deco
def myfunc(a,b,c):
ret = a+b+c
print('{}+{}+{}={}'.format(a,b,c,ret))
print('myfunc() called')
myfunc(1,2,3)
类似于第五步,只是用*args表示可变数量,**kwargs表示key-value形式的参数数量可变
第七步:让装饰器带参数
#!/usr/bin/env python
is_debug = True
def deco(arg):
def midwrapper(func):
def innerwrapper(*args,**kwargs):
print('before myfunc() called')
func(*args,**kwargs)
print('after myfunc() called')
return innerwrapper
return midwrapper
@deco(is_debug)
def myfunc(a,b,c):
ret = a+b+c
if is_debug:
print('{}+{}+{}={}'.format(a,b,c,ret))
print('myfunc() called')
myfunc(1,2,3)
>>>before myfunc() called
通过让装饰器带参数is_debug控制log的输出.
当is_debug = False时,输出为myfunc里面有一段代码没有log。
>>>before myfunc() called
>>>myfunc() called
>>>after myfunc() called
第八步:让装饰器带类参数
#!/usr/bin/env python
class locker:
def __init__(self):
print("locker.__init__() should be not called")
@staticmethod
def acquire():
print("locker.acquire() called")
@staticmethod
def release():
print("locker.release() called")
def deco(cls):
def midwrapper(func):
def innerwrapper(*args,**kwargs):
print("before {} called {}.".format(func.__name__,cls.__name__))
cls.acquire()
try:
func(*args,**kwargs)
finally:
cls.release()
return innerwrapper
return midwrapper
@deco(locker)
def myfunc(*args,**kwargs):
print('myfunc() called')
myfunc()
>>>before myfunc called locker.
#!/usr/bin/env python
class mylocker:
def __init__(self):
print('mylocker.__init__() called.')
@staticmethod
def acquire():
print('mylocker.acquire() called.')
@staticmethod
def unlock():
print('mylokcer.unlock() called.')
class lockerex(mylocker):
@staticmethod
def acquire():
print('lockerex.acquire() called.')
@staticmethod
def unlock():
print('lockerex.unlock() called.')
def lockhelper(cls):
def midwrapper(func):
def innerwrapper(*args, **kwargs):
print('before {} called.'.format(func.__name__))
cls.acquire()
try:
func(*args,**kwargs)
finally:
cls.unlock()
return innerwrapper
return midwrapper
class example:
@lockhelper(mylocker)
def myfunc(self):
print('myfunc() called')
@lockhelper(mylocker)
@lockhelper(lockerex)
def myfunc2(self,a,b):
print('{}+{}={}'.format(a,b,a+b))
print('myfunc2() called')
return a+b
if __name__ == '__main__':
a = example()
a.myfunc()
print('=='*15)
a.myfunc2(a=1,b=2)
>>>before myfunc called.
同时,我将code稍微改变了一下,运行结果也大大超出了我的预期,看来我需要好好再深入研究一下。
#!/usr/bin/env python
class mylocker:
def __init__(self):
print('mylocker.__init__() called.')
@staticmethod
def acquire():
print('mylocker.acquire() called.')
@staticmethod
def unlock():
print('mylokcer.unlock() called.')
class lockerex(mylocker):
@staticmethod
def acquire():
print('lockerex.acquire() called.')
@staticmethod
def unlock():
print('lockerex.unlock() called.')
def lockhelper(cls):
def midwrapper(func):
print('before {} called.'.format(func.__name__))
def innerwrapper(*args, **kwargs):
print('before {} called.'.format(func.__name__))
cls.acquire()
try:
func(*args,**kwargs)
finally:
cls.unlock()
return innerwrapper
return midwrapper
class example:
@lockhelper(mylocker)
def my2func(self):
print('myfunc() called')
@lockhelper(mylocker)
@lockhelper(lockerex)
def myfunc2(self,a,b):
print('{}+{}={}'.format(a,b,a+b))
print('myfunc2() called')
return a+b
if __name__ == '__main__':
a = example()
a.myfunc2(a=1,b=2)