本文改编自以下文章:Decorators in Python
装饰器是一个很强大的工具,它允许我们很便捷地修改已有函数或者类的功能,我们可以用装饰器把另一个函数包装起来,扩展一些功能而不需要去修改这个函数代码。
预备知识
在Python中,函数是第一类对象,也就是说,Python中的函数可以作为参数来使用或传递,它具有如下属性:
- 可以将函数存储在变量中
- 可以将函数作为参数传递给另一个函数
- 可以在函数中 return 另一个函数
- 可以将函数存储在数据结构中,如哈希表,列表等
【例 1】将函数视为对象
在上面例子中,我们把函数up
赋值给变量up1
。这个操作并不会调用函数,而只是给函数up
取了个别名up1
。
【例 2】将函数作为参数传递
上面例子中,函数dream
的参数up
和low
也是函数
【例 3】从函数中 return 函数
这边比较绕,注意create_adder(15)
返回的是一个函数,这个函数等同于adder
,并且其内参数x
是15。
最简单的装饰器
这边给出一个装饰器:
可以看到,装饰器就是把一个函数作为参数传进去,它本身也是个函数,把传进去的函数包装完之后,再把函数传出来。
我们把装饰器当成函数,正常调用就是上面这样。当然,装饰器有它特殊的用法,比如像这样:
它的工作逻辑就是把我定义的函数paper1
当成参数传递给我的装饰器decorator
,再传出来,当然我也可以把装饰器当成函数直接调用,就是麻烦一点罢了。
装饰器的语法逻辑是这样的:
python
@decorator
def func():
print("果壳小旋子")
'''Above code is equivalent to -
def func():
print("果壳小旋子")
func = decorator(func)'''
【例 4】计算代码运行时间的装饰器
python
# importing libraries
import time
import math
# decorator to calculate duration
# taken by any function.
def calculate_time(func):
# added arguments inside the inner1,
# if function takes any arguments,
# can be added like this.
def inner1(*args, **kwargs):
# storing time before function execution
begin = time.time()
func(*args, **kwargs)
# storing time after function execution
end = time.time()
print("Total time taken in : ", func.__name__, end - begin)
return inner1
装饰器的作用很明显了,当我需要计算某一个函数的运行时间时,我只要找到那个函数的定义,在前面@calculate_time
就行了,不需要再一个一个修改函数,当我不需要计算时间的时候,把装饰器删掉就行了。
被装饰的函数需要传参,有返回值
python
def hello_decorator(func):
def inner1(*args, **kwargs):
print("before Execution")
# getting the returned value
returned_value = func(*args, **kwargs)
print("after Execution")
# returning the value to the original frame
return returned_value
return inner1
上面的装饰器中的内层函数inner1
可以接收参数*args, **kwargs
,并且可以返回值returned_value
。*args
表示可以接受任意长度的位置参数,**kwargs
表示可以接受任意长度的关键字参数。
多个装饰器连用
如果我有多个装饰器来装饰同一个函数,会是什么情况?首先定义两个装饰器:
python
# code for testing decorator chaining
def decor1(func):
def inner():
x = func()
return x * x
return inner
def decor(func):
def inner():
x = func()
return 2 * x
return inner
装饰器decor
将函数返回值加倍,装饰器decor1
将函数返回值乘方
等价于
python
decor1(decor(num))
decor(decor1(num2))
想了解带有参数的Python装饰器,可以参考Python 带参数的装饰器