python之装饰器【转】
装饰器
先来看一个例子,一个函数f1接收另一个函数f2作为参数,并将该参数f2返回给f2:
def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
def myfunc():
print("myfunc() called.")
myfunc = deco(myfunc)
myfunc()
myfunc()
可以使用装饰器(Decorator)语法糖来简化代码:
def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
@deco
def myfunc():
print("myfunc() called.")
myfunc()
myfunc()
跟上面的代码完全一样,也就是说在定义一个函数(比如foo)的时候,加上@deco,就等同于:foo = deco(foo)
简单的看上面的代码似乎没什么价值,无非就是在Decorator中接收一个函数作为参数,并返回给作为参数的函数本身。
但如果我们在Decorator中内嵌定义一个新的函数,并将这个新的函数返回给作为参数的函数,那就不一样了,例如:
def deco(func):
def _newfunc():
print("before myfunc() called.")
func()
print("after myfunc() called.")
# 不需要返回func,实际上应返回原函数的返回值
return _newfunc
@deco
def myfunc():
print("myfunc() called.")
return 'ok'
myfunc()
在这里,myfunc()函数永久的失去了,后续调用myfunc(),真正调用的其实是在Decorator中返回的函数。
可见,Decorator实际上就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。
Decorator是在函数之上的修饰,这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用。
装饰带参数的函数
上面的例子Decorator装饰的参数都是无参的,下面来看如何装饰带参数的函数,
def deco(func):
def _newfunc(a, b):
print("before myfunc() called.")
ret = func(a, b)
print("after myfunc() called. result: %s" % ret)
return ret
return _newfunc
@deco
def myfunc(a, b):
print("myfunc(%s,%s) called." % (a, b))
return a + b
myfunc(1, 2)
如果被装饰函数的参数个数不确定呢,即可变参数的情况:
def deco(func):
def _newfunc(*args, **kwargs):
print("before %s called." % func.__name__)
ret = func(*args, **kwargs)
print("after %s called. result: %s" % (func.__name__, ret))
return ret
return _newfunc
@deco
def myfunc(a, b=8, c=1):
print("myfunc2(%s,%s,%s) called." % (a, b, c))
return a+b+c
myfunc(1)
myfunc(1, 4)
myfunc(1, 10, 9)
只要在装饰器中的内嵌函数使用可变参数列表(*args、**kwargs)即可。
装饰器自身带参数
这种情况需要嵌套两层函数:
def deco(arg):
def _deco(func):
def _newfunc():
print("before %s called [%s]." % (func.__name__, arg))
func()
print("after %s called [%s]." % (func.__name__, arg))
return _newfunc
return _deco
@deco("mymodule")
def myfunc():
print("myfunc() called.")
myfunc()
如上面的例子,执行myfunc 函数实际上是执行:deco("mymodule")(myfunc) ,deco("mymodule") 返回的是_deco函数,再调用该函数_deco(myfunc),最终返回的是_newfunc函数。
函数也是对象,有__name__属性,如果执行 myfunc.__name__ 语句,会发现返回的是 "_newfunc",如果要保留被装饰函数的原来名字,可以在装饰器中加入一句:@functools.wraps(func)
def deco(arg):
def _deco(func):
@functools.wraps(func)
def _newfunc():
print("before %s called [%s]." % (func.__name__, arg))
func()
print("after %s called [%s]." % (func.__name__, arg))
return _newfunc
return _deco
如果装饰器的参数是类类型:
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):
'''cls 必须实现acquire和release静态方法'''
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()
同时使用多个装饰器
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()
try:
return func(*args, **kwargs)
finally:
cls.unlock()
return __deco
return _deco
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
a = example()
a.myfunc()
print(a.myfunc())
print(a.myfunc2(1, 2))
print(a.myfunc2(3, 4))


浙公网安备 33010602011771号