【Python-装饰器】通过一个装饰器示例分析装饰器的本质及特性

无参数装饰器

装饰器的简单理解:

############装饰器写法
@decorate
def target():
    print('running target()')

############等同于
def target():
 print('running target()')
target = decorate(target) # 将被装饰的函数作为参数传递给装饰器函数。然后返回装饰器函数对象。


# 当调用被装饰器装饰的函数时,实际执行的是装饰器内部返回的内部函数对象

通过一个装饰器示例来展示装饰器的特性及本质:
python_decorator.py

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before function is called")
        result = func(*args, **kwargs)  # 调用原始函数
        print(f'func result is {result}')
        print("After function is called")
        return result
    return wrapper

@my_decorator
def add_numbers(a, b):
    print('running now add_numbers()')
    return a + b

result = add_numbers(3, 5)
print("Result:", result)

我们可以根据这段代码的运行流程来查看装饰器的特性:
在这里我们需要使用到https://pythontutor.com/render.html#mode=display这个网站。该网站可以一步一步的输出整段代码的运行流程

step_1. 在加载模块时,会先生成一个my_decorator装饰器函数对象。然后创建一个my_decorator函数变量指向my_decorator装饰器函数对象,类似这样my_decorator=my_decorator(func) 注意:这里是因为装饰器函数与被装饰器函数定义在同一个模块,Python会从上到下将变量或函数的定义保存到内存中,装饰器的实际执行的第一步应该是@my_decorator这部分。

step_2. 执行@my_decorator。首先,这里会将add_numbers(a, b)生成函数对象,然后将装饰器中的参数func指向add_numbers函数对象。然后再进入my_decorator装饰器函数内部,生成wrapper内部函数对象并return。

step_3. 重点:def add_numbers(a, b) 当执行到这一步的时候,正常情况下应该是生成acc_number=add_numbers(a,b),也就是acc_number这个函数变量指向add_numbers函数对象。但是因为装饰器的原因,实际的acc_number变量指向的是my_decorator这个装饰器函数中的内部函数对象wrapper。也就是说当你调用add_numbers函数时,它实际执行的是装饰器中的wrapper内部函数。

从整个装饰器的运行流程以及最后返回的结果来看。可以总结出装饰器的本质以及作用:

装饰器的定义:

  • 装饰器的本质就是一个包裹函数,用来修改或者增强被装饰的函数。
  • 装饰器是一种可调用对象,它的参数是被装饰的函数。
  • 装饰器可能会对被装饰的函数做相关处理,然后再返回该函数。或者会将被装饰的函数替换为另外一个函数或可调用对象。

装饰器的特性:

  • 装饰器是一个函数或者一个可调用对象。
  • 装饰器可以把被装饰的函数替换为其他的函数。
  • 装饰器会在所在模块被加载时运行。在流畅的Python中关于装饰器疑问中有提到装饰器的一个关键性质是,它们在被装饰的函数定义之后立即运行。这通常是在导入时(例如,当 Python 加载模块时)。
posted @ 2023-09-01 15:16  白猫打不过黑猫  阅读(10)  评论(0编辑  收藏  举报