在Python中,装饰器(Decorator) 使用 @
符号实现,是一种修改函数/类行为的语法糖。它本质上是一个高阶函数,接受目标函数作为参数并返回包装后的函数。Python也提供了多个内置装饰器,如 @property
、@staticmethod
、@classmethod
等。
一、核心概念
- 装饰器本质:
@decorator
等价于func = decorator(func)
- 执行时机:在函数/类定义时立即执行装饰器逻辑
- 链式装饰:多个装饰器按从下到上的顺序应用
@decorator1 @decorator2 def func(): ... # 等价于:func = decorator1(decorator2(func))
二、自定义装饰器示例
1. 函数装饰器(无参数)
def logger(func):
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@logger
def add(a, b):
return a + b
print(add(3, 5))
# 输出:
# 调用函数: add
# 8
2. 带参数的装饰器
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# 输出:
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
3. 类装饰器
class CountCalls:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"调用次数: {self.calls}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello() # 输出: 调用次数: 1 \n Hello!
say_hello() # 输出: 调用次数: 2 \n Hello!
三、内置装饰器详解
1. @property
:定义属性访问
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""Getter: 访问半径"""
return self._radius
@radius.setter
def radius(self, value):
"""Setter: 设置半径(带验证)"""
if value <= 0:
raise ValueError("半径必须为正数")
self._radius = value
@property
def area(self):
"""只读属性: 计算面积"""
return 3.14 * self._radius ** 2
c = Circle(5)
print(c.radius) # 5 (像属性一样调用)
c.radius = 10 # 调用setter
print(c.area) # 314.0 (只读属性)
# c.area = 100 # 报错: AttributeError
2. @classmethod
:类方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str):
"""工厂方法: 从字符串创建实例"""
year, month, day = map(int, date_str.split('-'))
return cls(year, month, day) # cls指向类本身
def __str__(self):
return f"{self.year}-{self.month}-{self.day}"
d = Date.from_string("2023-10-01")
print(d) # 2023-10-01
3. @staticmethod
:静态方法
class MathUtils:
@staticmethod
def add(a, b):
"""与类相关但不需要类/实例参与的独立函数"""
return a + b
print(MathUtils.add(3, 7)) # 10 (无需实例化)
4. @functools.wraps
:保留元数据
import functools
def debug(func):
@functools.wraps(func) # 保留原函数名/文档等元数据
def wrapper(*args, **kwargs):
print(f"调试: {func.__name__}()")
return func(*args, **kwargs)
return wrapper
@debug
def example():
"""示例函数文档"""
pass
print(example.__name__) # "example" (不加wraps会显示"wrapper")
print(example.__doc__) # "示例函数文档"
四、使用场景总结
装饰器类型 | 典型应用场景 |
---|---|
自定义无参装饰器 | 日志记录、性能计时、权限验证 |
自定义带参装饰器 | 重复执行、超时控制、注册路由(如Flask) |
@property |
封装属性访问、添加验证逻辑、计算属性 |
@classmethod |
工厂方法创建实例、操作类级别数据 |
@staticmethod |
工具函数(与类相关但无需访问实例/类状态) |
@functools.cache |
缓存函数结果(Python 3.9+) |
五、注意事项
- 装饰器会改变函数的
__name__
等元数据(用@functools.wraps
修复) - 多个装饰器顺序影响行为(最靠近函数的装饰器最先执行)
@property
必须定义在相同名称的@xxx.setter
之前
掌握装饰器能大幅提升代码的复用性和可读性,是Python高级编程的核心技术之一。