python中的装饰器语法[Decorator]
函数装饰器
def decor_func(func):
def wrapper(x):
print("wrapper in")
result = func(x)
print("wrapper out")
return result
return wrapper
@decor_func
def do_something(x):
print("do something")
return x * 2
print(do_something(3))
def decor_func(func):
def wrapper(*args, **kwargs):
print("wrapper in")
result = func(*args, **kwargs)
print("wrapper out")
return result
return wrapper
def do_something(x):
print("class method")
return x * 2
do_something = decor_func(do_something)
print(do_something(3))
左右脚本功能相同,左边使用了装饰器语法,执行脚本的输出:
wrapper in do something wrapper out 6
解释一下右侧代码
- decor_func将do_something做为参数传入,在decor_func内部do_something的函数入口地址没变,只不过它现在叫func了,也就是换了一个名字。
- decor_func内部定义了一个wrapper的函数,并返回wrapper函数的入口地址做为函数decor_func的返回值,在dector_func外部,wrapper叫do_something,同样是换了一名字,内部的入口地址还是没变。
- 所在当调用do_something时,实际上是在使用wrapper函数。
类方法的装饰器
相比函数的装饰器,这要复杂一些,主要分为下面三步:
1. 定义类和方法
2. 使用装饰器“装饰”该方法,装饰器会返回一个替代方法
3. 将替代方法绑定到类上,替换原方法
def decor_func(func):
def wrapper(*args, **kwargs):
print("wrapper in")
result = func(*args, **kwargs)
print("wrapper out")
return result
return wrapper
class Example:
def __init__(self, x=1):
self.val = x
@decor_func
def do_something(self):
print("class method")
return self.val * 2
a= Example(2)
print(a.do_something())
def decor_func(func):
def wrapper(*args, **kwargs):
print("wrapper in")
result = func(*args, **kwargs)
print("wrapper out")
return result
return wrapper
class Example:
def __init__(self,x=1):
self.val=x
def do_something(self):
print("class method")
return self.val * 2
Example.do_something = decor_func(Example.do_something)
a=Example(2)
print(a.do_something())
上面两段代码功能相同,右边红色部分等同于左边使用装饰器语法,其输出:
wrapper in class method wrapper out 4
PS:
*args表示wrapper函数可以接收任意多个位置参数,这些参数会被收集到一个元组中,名为args。
**kwargs表示wrapper函数可以接收任意多个关键字参数,这些参数会被收集到一个字典中,名为kwargs。
这使得wrapper函数变得非常灵活,可以处理任意参数传递的情况。在装饰器的wrapper中尤其有用,因为它需要接受原函数func可能接收的任意参数。
类的装饰器
python
def count_objects(cls):
cls.num_instances = 0
def new_init(self, *args, **kwargs):
cls.num_instances += 1
cls.__init__(self, *args, **kwargs)
cls.__init__ = new_init
return cls
@count_objects
class Example:
def __init__(self, name):
self.name = name
类的装饰器可以用于修改类,如上面的例子中我们对Example类添加了一个计数器。
当我们使用@语法应用count_objects装饰器时,会发生以下过程:
1. Example类首先被定义
2. count_objects函数被调用,并将Example类本身做为参数传入cls,这时在count_objects函数中cls就是Example本身。
3. 在count_objects函数内,cls引用的就是Example类。我们在函数中添加了num_instances属性,并修改了构造方法,这实际上就是在修改Example类的定义。
4. count_objects函数返回Example类,也就是将修改后的Example类Definition返回
5. 新返回的Example类定义替代了原来的类定义,成为@count_objects之后的Example类的定义
6. 所以,最终我们得到的Example类中包含了count_objects函数添加的属性和方法变更