Decorators Pattern in Python
装饰器模式(Decorator Pattern)是一种设计模式,它允许你通过将对象放入包装对象中来为原对象添加新的行为。装饰器模式是一种替代继承的技术,它通过一种无需子类化增加功能的方式来扩展类的功能。
使用 decorators,可以在不直接修改源码的情况下修改 function 或 method 的功能,从而让代码更简洁,更 Pythonic
The fundamentals
functions in python
python 中函数是 first-class object,可以赋值给变量、作为参数传递、作为返回值、在运行时动态创建和修改,结合内置的装饰器,可以方便地实现横切关注点,无需额外的 AOP 库。
First-Class Object
- being expressible as an anonymous literal value
- being storable in variables
- being storable in data structures
- having an intrinsic identity (independent of any given name)
- being comparable for equality with other entities
- being passable as a parameter to a procedure/function
- being returnable as the result of a procedure/function
- being constructible at runtime
- being printable
- being readable
- being transmissible among distributed processes
- being storable outside running processes
如下是使用 Python 中函数特性的例子:
PYTHON
# 1. Assigning Functions to Variablessay_hello = greetprint(say_hello("Alice")) # Output: Hello, Alice!# 2. Pass function as an argumentdef 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 multiplierdouble = make_multiplier(2)print(double(5)) # Output: 10
Simple Demo
结合以上特性,可以实现一个简单的装饰器,实现计时功能
Tips: 建议使用 functools.wraps
保留原函数的元信息,如__name__、__doc__
等
PYTHON
from functools import wrapsfrom time import timedef 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_decoratordef calculate(): sum = 0 for i in range(1000000): sum += i print(sum)# 上述代码等价于# @simple_decorator syntax 等价于 calculate = simple_decorator(calculate)calculate()[OUTPUT]function <calculate> is called499999500000Function execution time: 0.16027212142944336
Chaining Decorators
Python 允许使用多个装饰器,可以通过 @decorator1
@decorator2
… @decoratorN
的方式来实现
PYTHON
from functools import wrapsdef star(func): @wraps(func) def wrapper(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return wrapperdef hyphen(func): @wraps(func) def wrapper(*args, **kwargs): print("-" * 30) func(*args, **kwargs) print("-" * 30) return wrapper@star@hyphendef greet(name): print(f"Hello, {name}!")greet("Python")[OUTPUT]******************************------------------------------Hello, Python!------------------------------******************************
Best Practices(?)
- Parameter Validation Decorators
- Method Routing
- Caching and Memoization
- …
@lru_cache
PYTHON
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
PYTHON
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 = valueperson = Person("Alice", 25)print(person.name) # Output: Aliceperson.name = "Bob"print(person.name) # Output: Bob
Ref
Decorators Pattern in Python