【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 加载模块时)。