欢迎访问我的网站,希望内容对您有用,感兴趣的可以加入我们的社群。

Python中的装饰器

Python基础 迷途小书童 1年前 (2023-09-04) 586次浏览 0个评论

装饰器是一个非常有用而又常被误解的功能,可以让我们在不修改函数或类的源代码情况下给它们提供扩展功能。本文将通过具体示例带你深入理解 Python 装饰器的用法。

装饰器基础

装饰器本质上是一个函数,它可以让其他函数在不需要做任何代码变动的前提下添加额外功能。装饰器的语法如下

@decorator 
def func():
    pass

这里的 @decorator 就表示使用 decorator 这个装饰器来装饰后面的函数。

我们来看一个具体的例子

from functools import wraps

def log(func):
    @wraps(func)  
    def wrapper(*args, **kwargs):
        print(f"Call {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def add(x, y):
    return x + y

print(add(2, 3))

执行上述代码,输出结果为

Call add
5

这里我们定义了一个名为 log 装饰器,它会打印函数名称然后再调用原函数。通过 @log 就可以来装饰 add 函数,使其获得打印日志的功能。

带参数的装饰器

装饰器本身也可以带参数,需要多一层封装

from functools import wraps

def repeat(num):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(num):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(num=3)
def greet(name):
    print(f"Hello {name}")

greet("xgx")

执行上述脚本,输出结果为

Hello xgx
Hello xgx  
Hello xgx

这种带参数的装饰器在一些特殊场景下非常有用,如需要自定义执行的次数。

装饰类

装饰器不仅可以装饰函数,还可以装饰类,看下面的示例

from functools import wraps

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello(name):
    print(f"Hello {name}")

say_hello("xgx")
say_hello("Alice")

执行上述脚本,输出结果为

Call 1 of 'say_hello'
Hello xgx
Call 2 of 'say_hello'
Hello Alice

这里我们定义了一个 CountCalls 类,实现了 __init__()__call__() 方法。使用 @CountCalls 装饰 say_hello 函数时,会先创建 CountCalls 实例对象,并将 say_hello 函数存入实例的 func 属性。在调用 say_hello 时,实际上调用的是 CountCalls 实例对象,它会更新调用次数,打印信息,最后再调用原始的 say_hello 函数。这样就实现了一个统计调用次数的装饰器。

类装饰器的好处是可以存储状态,方便扩展额外的功能。

多个装饰器

多个装饰器可以层层嵌套,执行顺序由里到外。

@decorator1
@decorator2
def func():
    pass

例如

@repeat(num=3) 
@log
def greet(name):
    print(f"Hello {name}")

greet 函数先由 @log 装饰,然后由 @repeat 装饰。

所以装饰器的顺序会影响函数的行为。

总结

装饰器是一个非常强大和有用的功能,可以让我们在不修改源代码的情况下动态扩展函数和类的功能,是每个 Python 程序员都应该掌握的重要知识点。

喜欢 (0)

您必须 登录 才能发表评论!