python中的装饰器语法[Decorator]

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

解释一下右侧代码

  1. decor_func将do_something做为参数传入,在decor_func内部do_something的函数入口地址没变,只不过它现在叫func了,也就是换了一个名字。
  2. decor_func内部定义了一个wrapper的函数,并返回wrapper函数的入口地址做为函数decor_func的返回值,在dector_func外部,wrapper叫do_something,同样是换了一名字,内部的入口地址还是没变。
  3. 所在当调用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函数添加的属性和方法变更

Comments are closed.