2.23. 装饰器¶
装饰器是一种把一个函数(或方法)包装进另一个函数的语法。包装器可以在原始调用前后添加行为、替换返回值,或附加元数据。其形式为:
@wrapper
def f():
...
@wrapper 这一行等价于 f = wrapper(f) ——函数 f 正常构建,然后被交给 wrapper,其结果被重新绑定到名称 f。
装饰器接收一个函数并返回一个新函数。¶
2.23.1. 内置方法装饰器¶
Python 自带了几个装饰器,用在类体内部。
2.23.1.1. @property¶
把一个方法变成计算属性。调用者像访问普通属性那样访问它(不带括号),但每次读取时都会运行一个方法:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius * self.radius
c = Circle(5)
print(c.area) # 78.53975 -- no parentheses
当一个看起来简单的属性背后需要做一点小计算时,请使用它。如果计算开销很大,则更应使用普通方法——调用者不会预期读取属性会很慢。
2.23.1.2. @classmethod¶
定义一个接收类而非实例作为第一个参数的方法。第一个参数按惯例命名为 cls:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def origin(cls):
return cls(0, 0)
p = Point.origin()
类方法是提供替代构造函数的标准方式——即以非默认方式返回构建好的实例的工厂函数。
2.23.1.3. @staticmethod¶
定义一个既不接收实例也不接收类的方法——它只是一个出于组织原因而存在于类命名空间中的普通函数:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
请谨慎使用它;如果一个函数确实与类的状态毫无关系,那么放在模块级别的普通函数通常更清晰。
2.23.2. 编写自定义装饰器¶
装饰器是一个接收函数并返回函数的函数。其最简形式:
def log_calls(func):
def wrapper(*args, **kwargs):
print("calling", func.__name__)
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
add(2, 3)
输出::
calling add
wrapper 闭包捕获了 func 并将一切转发给它。*args / **kwargs 让它能作用于任何函数,而不只是双参数的函数。这种模式是更精巧装饰器(计时、缓存、失败重试)的基础,但其核心始终不变:接收一个函数,返回一个函数。