2023/08/15

py修饰器

Python 装饰器 (Decorator)

参考视频: Bilibili - Python装饰器

1. 理解函数作为对象

在 Python 中,函数是一等公民 (First-class Citizen),这意味着函数可以像其他任何对象(如整数、字符串、列表)一样被对待:

  • 可以被赋值给一个变量。

  • 可以作为参数传递给另一个函数。

  • 可以作为另一个函数的返回值。

这个特性是实现装饰器的基础。

输出:


输出:

2. 函数装饰器

装饰器本质上是一个可调用对象 (callable),它接收一个函数作为输入,并返回一个新的函数(或可调用对象)作为输出。它允许我们在不修改原函数代码的情况下,为函数增加额外的功能。

语法糖 @ 的工作原理如下:

这行代码完全等价于:

基础函数装饰器

输出:

注意,"Decorating function..." 这句话在程序加载(定义函数)时就会打印,而不是在调用 double(5) 时。

带有内部包装函数的装饰器

为了在每次调用被装饰函数时都执行某些操作(如计时、日志记录),我们需要在装饰器内部定义一个包装函数 wrapper,并返回这个包装函数。

*args**kwargs 用于确保装饰器可以处理任意参数的函数。

输出:

带参数的装饰器

如果想让装饰器本身接收参数,例如 @my_decorator(arg),我们需要再嵌套一层函数。

@timer_decorator(iterations=1000) 的执行过程等价于:

输出:


3. 类装饰器

装饰器不仅可以是函数,也可以是类。这在需要维护状态(如计数)时特别有用。

类作为装饰器

要让一个类能作为装饰器,它必须实现 __init____call__ 这两个魔术方法。

  • __init__(self, func): 在装饰时调用,接收被装饰的函数 func 作为参数。

  • __call__(self, *args, **kwargs): 在每次调用被装饰后的函数时执行。

输出:

可以看到,greet 变量现在实际上是一个 CallCounter 的实例,但它仍然可以像函数一样被调用。

装饰一个类

装饰器也可以用来装饰整个类。这允许我们修改或增强类的行为。

示例1:给类添加属性

输出:

示例2:包装类的方法

这个例子展示了如何通过装饰器包装一个类,并修改其某个方法的行为。

输出:

注意: calc 对象的类型现在是 log_method_calls.<locals>.Wrapper,而不是 Calculator。这种方式虽然强大,但也更复杂,有时直接使用继承或元类是更好的选择。

附:Python中几个内置的类装饰器

  • @staticmethod: 将一个方法转换为静态方法。静态方法不接收隐式的第一个参数(selfcls),它就像一个定义在类命名空间内的普通函数。

  • @classmethod: 将一个方法转换为类方法。类方法的第一个参数是类本身,通常命名为 cls

  • @property: 将一个方法转换为只读属性。可以让你像访问属性一样调用一个方法,而不需要加括号。

0 评论:

发表评论