装饰器
通过一个简单的装饰器示例来展示装饰器的特性及本质:
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")
result= 20
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这个网站。该网站可以一步一步的输出整段代码的运行流程
1、在加载模块时,可以看到第一步会先执行my_decorator
这个装饰器函数的定义。然后将其存储在内存中。注意:这里是因为装饰器函数与被装饰器函数定义在同一个模块,Python会从上到下将变量或函数的定义保存到内存中,装饰器的实际执行的第一步应该是@my_decorator这部分。
2、随后会执行@my_decorator
部分,这部分其实是将add_numbers()
这个被装饰的函数作为参数传递给my_decorator(func)
这个装饰器函数。
3、然后进入my_decorator
装饰器函数,会执行wrapper()
这个内部函数的定义。然后return wrapper
函数对象。
4、result = add_numbers()
,调用 add_numbers()
被装饰函数时,实际会跳转到装饰器函数 my_decorator
return的内部函数 wrapper()
中。然后开始执行 wrapper()
这个内部函数,
5、result = func(*args, **kwargs)
, 在 wrapper()
这个内部函数中调用了原始函数,也就是 add_numbers()
这个函数。然后将 add_numbers()
的return赋值给result
这个变量。这时result
的值为3+5=8。随后又将20赋值给result
这个变量并进行返回。
6、print("Result:", result)
当正常运行add_numbers()
函数时,本来应该是输出3+5=8的,但是因为装饰器的原因,运行add_numbers()
函数时实际进入的是装饰器中的返回的内部函数wrapper()
,而在内部函数wrapper()
中我将result
的结果修改为20,所以无论参数传的是多少,最后的resut
都将是20。
从整个装饰器的运行流程以及最后返回的结果来看。可以很清晰的总结出装饰器的本质以及作用:
装饰器的定义:
- 装饰器的本质就是一个包裹函数,用来修改或者增强被装饰的函数。
- 装饰器是一种可调用对象,它的参数是被装饰的函数。
- 装饰器可能会对被装饰的函数做相关处理,然后再返回该函数。或者会将被装饰的函数替换为另外一个函数或可调用对象。
装饰器的特性:
- 装饰器是一个函数或者一个可调用对象。
- 装饰器可以把被装饰的函数替换为其他的函数。
- 装饰器会在所在模块被加载时运行。