Decorators Pattern in Python
装饰器模式(Decorator Pattern)是一种设计模式,它允许你通过将对象放入包装对象中来为原对象添加新的行为。装饰器模式是一种替代继承的技术,它通过一种无需子类化增加功能的方式来扩展类的功能。
使用 decorators,可以在不直接修改源码的情况下修改 function 或 method 的功能,从而让代码更简洁,更 Pythonic
The fundamentals
functions in python
python 中函数是 first-class object,可以赋值给变量、作为参数传递、作为返回值、在运行时动态创建和修改,结合内置的装饰器,可以方便地实现横切关注点,无需额外的 AOP 库。
如下是使用 Python 中函数特性的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. Assigning Functions to Variables
say_hello = greet
print(say_hello("Alice")) # Output: Hello, Alice!
# 2. Pass function as an argument
def apply_function(func, value):
return func(value)
def uppercase(text):
return text.upper()
result = apply_function(uppercase, "hello")
print(result) # Output: HELLO
# 3. Return function from another function (Nested Function)
def make_multiplier(n):
def multiplier(x):
return x * n
return multiplier
double = make_multiplier(2)
print(double(5)) # Output: 10
Simple Demo
结合以上特性,可以实现一个简单的装饰器,实现计时功能
Tips: 建议使用 functools.wraps
保留原函数的元信息,如__name__、__doc__
等
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
30
31
from functools import wraps
from time import time
def simple_decorator(func):
@wraps(func) # 此注解用于保留func的元信息
def wrapper(*args, **kwargs):
print(f"function <{func.__name__}> is called")
start_time = time()
result = func(*args, **kwargs)
print(f"Function execution time: {time() - start_time}")
return result
return wrapper
@simple_decorator
def calculate():
sum = 0
for i in range(1000000):
sum += i
print(sum)
# 上述代码等价于
# @simple_decorator syntax 等价于 calculate = simple_decorator(calculate)
calculate()
[OUTPUT]
function <calculate> is called
499999500000
Function execution time: 0.16027212142944336
Chaining Decorators
Python 允许使用多个装饰器,可以通过 @decorator1
@decorator2
… @decoratorN
的方式来实现
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
30
31
32
33
34
35
from functools import wraps
def star(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return wrapper
def hyphen(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("-" * 30)
func(*args, **kwargs)
print("-" * 30)
return wrapper
@star
@hyphen
def greet(name):
print(f"Hello, {name}!")
greet("Python")
[OUTPUT]
******************************
------------------------------
Hello, Python!
------------------------------
******************************
Best Practices(?)
- Parameter Validation Decorators
- Method Routing
- Caching and Memoization
- …
@lru_cache
1
2
3
4
5
6
7
8
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
getter/setter
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
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def age(self):
return self._age
@age.setter
def age(self, value):
self._age = value
person = Person("Alice", 25)
print(person.name) # Output: Alice
person.name = "Bob"
print(person.name) # Output: Bob
Ref
Decorators Pattern in Python