绝大多数装饰器都是基于函数和 闭包 实现的,但这并非制造装饰器的唯一方式.
Python 对某个对象是否能通过装饰器(@decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象1
2
3def foo(): pass
type(foo) # function
callable(foo) # True
只要自定义类的 __call__ 魔法方法即可让任意类变成可被调用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18from dataclasses import dataclass
@dataclass()
class X:
a:int
b:int
range:int
def __call__(self): # 定义类的 `__call__` 方法
print('__call__ with ({}, {})'.format(self.a, self.b))
def __del__(self):
del self.a
del self.b
del self.range
x = X(1,2,3)
x() # 像函数一样调用
1 | import time |
1 |
|
wrapt 模块是一个专门帮助编写装饰器的工具库。可以非常方便的改造 provide_number 装饰器,完美解决“嵌套层级深”和“无法通用”两个问题1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import wrapt
import random
def provide_number(min_num, max_num):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
# 参数含义:
#
# - wrapped:被装饰的函数或类方法
# - instance:
# - 如果被装饰者为普通类方法,该值为类实例
# - 如果被装饰者为 classmethod 类方法,该值为类
# - 如果被装饰者为类/函数/静态方法,该值为 None
#
# - args:调用时的位置参数(注意没有 * 符号)
# - kwargs:调用时的关键字参数(注意没有 ** 符号)
#
num = random.randint(min_num, max_num)
# 无需关注 wrapped 是类方法或普通函数,直接在头部追加参数
args = (num,) + args
return wrapped(*args, **kwargs)
return wrapper
@provide_number(1,100)
def number(num):
print(num)
number()
1 | import time |
1 | def add(a, b): |