Python 装饰器 Decorator
· 4 min read
![DDdl5A](https://cosmos-x.oss-cn-hangzhou.aliyuncs.com/DDdl5A.png)
https://www.bilibili.com/video/BV1Ah8jeQEeV
预备知识
- Python 中绝大多数元素都是对象 Object
- 类和函数也不例外,变量名都可以被重新赋值
def hello():
print('Hello')
def world():
print('World')
# 可以给 hello 赋值成另外一个函数 world
hello = world
hello()
# 甚至可以给 hello 赋值成 1
hello = 1
装饰器的形成
hello
赋值成 world
或 1
显然没有太多实际意义,我们能不能利用函数变量可以再被复制特性,做点更有意义的事情呢?Python 的设计师们想到的是对 hello
进行一些包装,增加一些 hello
原本没有的功能,比如记录函数运行的时间:
def timing(f):
return f
def hello():
print('Hello, World')
hello = timing(hello)
hello()
上面这样写等于什么都没有做,hello
-> f
-> hello
, 要想扩展 hello
函数的功能,得延迟对 f
的调用,那就需要一个 wrapper
函数去包裹 f
,同时也就形成了一个闭包:
import time
def timing(f):
def wrapper():
return f()
return wrapper
def hello():
print('Hello, World')
# hello 函数指向 wrapper 函数
hello = timing(hello)
hello()
有了 wrapper
函数,我们就可以来实现 timing
函数的真正功能了:
import time
def timing(f):
def wrapper():
start_time = time.time()
result = f() # 将 f 的返回值先存起来,最后再 return 出来,这样看上去 timing 函数 并没有影响 hello 函数原本的返回值
print(f'Total time: {time.time() - start_time}')
return result
return wrapper
def hello():
print('Hello, World')
hello = timing(hello)
hello()
所以,Python 针对这种情况,推出了一个语法糖 @
:
import time
def timing(f):
def wrapper():
start_time = time.time()
# 将 f 的返回值先存起来,最后再 return 出来
# 这样看上去 timing 函数并没有影响 hello 函数原本的返回值
result = f()
print(f'Total time: {time.time() - start_time}')
return result
return wrapper
@timing # 等同于 hello = timing(hello)
def hello():
print('Hello, World')
hello()
支持参数
如何让 timing
装饰器支持有参数的函数?
import time
def timing(f):
def wrapper(*args, **kwargs): # 用可变参数和关键字参数,让 wrapper 支持所有参数组合
start_time = time.time()
result = f(*args, **kwargs)
print(f'Total time: {time.time() - start_time}')
return result
return wrapper
@timing
def hello():
print('Hello, World')
hello()
def greet(name):
print(f'Hello {name}')
greet('kimi')
装饰类
import time
def timing(f):
def wrapper(*args, **kwargs): # 用可变参数和关键字参数,让 wrapper 支持所有参数组合
start_time = time.time()
result = f(*args, **kwargs)
print(f'Total time: {time.time() - start_time}')
return result
return wrapper
@timing # 等同于 Foo = timing(Foo)
class Foo:
def __init__(self):
pass
Foo()