目录
1.函数装饰器
函数装饰器允许修改或增强函数或方法的行为,而不需要改变其实际的代码。它接收一个函数作为参数,并返回一个新的函数(可以是原函数本身或新的可调用对象)。可以用来为原始函数添加一些额外的功能。
1.1语法
def decorator(F): #接受一个被装饰的函数参数,使用@decorator时调用
#添加额外操作
return F #可以返回一个不同的可调用对象,与F有相同数目的参数
@decorator # func = decorator(func)
def func():
...
func() # 相当于decorator(func)(),其中decorator(func)在@decorator语句已运行了
1.2实现
1)使用嵌套函数实现
def decorator(func):
def wrapper(*args,**kwargs):
print("before func")
func(*args,**kwargs)
print("after func")
return wrapper #返回了一个不同的可调用对象
@decorator
def say_hello(name):
print("Hello " + name)
say_hello("everyone")
#输出结果:
before func
Hello everyone
after func
2)使用类实现
class decorator:
def __init__(self,func): #运行@decorator时,绑定函数到装饰器类的实例
self.func = func
def __call__(self,*args): #调用被装饰函数时,实际为装饰器实例的调用
#可添加额外操作
self.func(*args)
@decorator # say_hello = decorator(say_hello),创建了一个装饰器实例
def say_hello(name):
print("Hello " + name)
say_hello("everyone")
注意方法装饰
用嵌套函数实现的装饰器可用于装饰方法,但用类实现的装饰器不能应用于方法。
class Decorator:
def __init__(self,func):
self.func = func
def __call__(self,*args):
self.func(*args) #调用失败,C实例不在args中
class C:
@Decorator
def say_hello(self,name):
print("Hello " + name)
C().say_hello("name")
当装饰器的方法__call__运行后,self接收Decorator类的实例,而args中只一个“name”参数,不包含类C的实例, 在调用方法say_hello时时缺少参数,调用失败。
1.3 保留原函数信息
@functools.wraps 可以复制原始函数的元信息到装饰器返回的新函数中,保留原函数的属性 。
from functools import wraps
def decorator(f):
@wraps(f)
def wrapper(*args,**kwargs):
print("calling decorated function")
return f(*args,**kwargs)
return wrapper
@decorator
def example():
'''
DocString example
'''
print("called function example")
example()
print(example.__name__)
print(example.__doc__)
#输出:
calling decorated function
called function example
example
DocString example
2.类装饰器
类装饰和函数装饰器使用相同的语法和相似的编程方式,但它是管理类的一种方式,使用额外逻辑来完成实例构造调用的一种方式。
2.1语法及实现
@decorator
class C: #等效于C = decorator(C)
pass
示例:装饰器创建并返回一个类
def decorator(cls):
class Wrapper:
def __init__(self,*args):
self.wrapper = cls(*args) #调用被装饰类创建实例
def __getattr__(self, item):
return getattr(self.wrapper, item) #获取属性时从被装饰的类的实例获取
return Wrapper
@decorator
class Person:
def __init__(self,name):
self.name = name
def say_hello(self):
print("Hello " + self.name)
a = Person("A")
b = Person("B")
a.say_hello()
b.say_hello()
print(a.name)
print(b.name)
#输出:
Hello A
Hello B
A
B
这个例子使用基于函数的方式(类工厂函数)创建装饰器,可支持多个实例,因为每次使用都会创建一个新的Wrapper对象和和被装饰类实例 。
错误示例:不支持多个实例
class Decorator:
def __init__(self,cls):
self.cls = cls
def __call__(self, *args, **kwargs):
self.wrapper = self.cls(*args,**kwargs) #Person实例覆盖上一个实例
return self
def __getattr__(self, item):
return getattr(self.wrapper, item)
@Decorator
class Person:
def __init__(self,name):
self.name = name
def say_hello(self):
print("Hello " + self.name)
a = Person("A")
b = Person("B")
a.say_hello()
b.say_hello()
print(a.name)
print(b.name)
#输出:
Hello B
Hello B
B
B
因为Decorator只有一个实例,所以新的Person实例赋值给sefl.wrapper时,会覆盖上一个实例 。
而上一例中,类似工厂函数,调用被装饰类创建实例时,会调用Wrapper的__init__生成新实例。
2.2重定义运算符重载
因装饰器返回的是一个新类,内置操作需要在装饰器中重新定义运算符重载方法,关联到原始的类。
示例:
def decorator(cls):
class OnInstance:
def __init__(self,*args,**kwargs):
self.wrapped = cls(*args,**kwargs)
#重定义重载运算符,指向被装饰类
def __str__(self):
return str(self.wrapped)
return OnInstance
@decorator
class Example:
def __init__(self,name):
self.name = name
def __str__(self):
return "my name is " + self.name
e1 = Example("e1")
print(e1)
还可以通过定义一个OnInstance的混入父类(Mixin)实现。
3.装饰器嵌套
装饰器按照被应用的顺序从内到外执行。类似套娃从内到外一层一层套。
@A
@B
@C
def f():
...
运行等效于:A(B(C(f)))
示例:
def decorator1(F):
def wrapper():
print('decorator1 before')
F()
print('decorator1 after')
return wrapper
def decorator2(F):
def wrapper():
print('decorator2 before')
F()
print('decorator2 after')
return wrapper
def decorator3(F):
def wrapper():
print('decorator3 before')
F()
print('decorator3 after')
return wrapper
@decorator1
@decorator2
@decorator3
def say():
print("yes")
#输出结果:
decorator1 before
decorator2 before
decorator3 before
yes
decorator3 after
decorator2 after
decorator1 after
4.装饰器参数
装饰器参数通常用来保持状态信息供随后的调用使用。通过创建装饰器工厂函数把参数传给它,并返回一个装饰器。
示例:
def decorator(A,B):
#保存或使用A,B
def actual_decorator(F):
#保存或使用函数F
def wrapper(*args):
return F(*args)
return wrapper
return actual_decorator
@decorator(A,B)
def F(*arg):
...
#等效于
F = decorator(A,B)(*arg)