Python装饰器详解和默认装饰器
文章目录
- python装饰器
- Python装饰器详解
- 装饰器的基本结构
- 带参数的装饰器
- 保留原始函数元信息
- 类装饰器
- 常见应用场景
- 默认装饰器
- **1. `@staticmethod` - 静态方法**
- **2. `@classmethod` - 类方法**
- **3. `@property` - 属性装饰器**
- **4. `@functools.lru_cache` - 缓存装饰器**
- **5. `@functools.wraps` - 保留函数元信息**
- **6. `@contextlib.contextmanager` - 上下文管理器**
- **7. `@dataclass` - 数据类装饰器(Python 3.7+)**
- **8. `@abc.abstractmethod` - 抽象方法(Python的抽象基类)**
- **总结**
python装饰器
Python装饰器详解
装饰器是Python
中一种强大的语法糖,允许你在不修改原有函数代码的情况下,扩展其功能。装饰器本质上是一个函数,它接收另一个函数作为参数,并返回一个新的函数。
装饰器的基本结构
一个简单的装饰器可以这样实现:
def my_decorator(func):def wrapper(*args, **kwargs):# 在调用原始函数之前执行的代码print("在函数执行前做一些事情")# 调用原始函数result = func(*args, **kwargs)# 在调用原始函数之后执行的代码print("在函数执行后做一些事情")return resultreturn wrapper@my_decorator
def say_hello(name):print(f"你好, {name}!")return f"已向{name}打招呼"# 调用被装饰的函数
result = say_hello("张三")
print(result)
上面的代码中:
my_decorator
是一个装饰器函数,它接收一个函数作为参数wrapper
是一个内部函数,它接收任意数量的位置参数和关键字参数*args
和**kwargs
确保装饰器可以应用于任何函数- 装饰器返回
wrapper
函数,替代了原始函数
带参数的装饰器
如果需要传递参数给装饰器,可以创建一个返回装饰器的函数:
def repeat(n):def decorator(func):def wrapper(*args, **kwargs):results = []for _ in range(n):result = func(*args, **kwargs)results.append(result)return resultsreturn wrapperreturn decorator@repeat(3)
def greet(name):return f"你好, {name}!"# 调用被装饰的函数
messages = greet("李四")
print(messages) # 输出: ['你好, 李四!', '你好, 李四!', '你好, 李四!']
保留原始函数元信息
当使用装饰器时,原始函数的元信息(如名称、文档字符串等)会被替换为包装器的信息。可以使用 functools.wraps
来保留这些信息:
import functoolsdef my_decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):print(f"调用函数 {func.__name__}")result = func(*args, **kwargs)print(f"函数 {func.__name__} 执行完毕")return resultreturn wrapper@my_decorator
def add(a, b):"""计算两个数的和"""return a + bprint(add.__name__) # 输出: add
print(add.__doc__) # 输出: 计算两个数的和
类装饰器
除了函数装饰器,还可以使用类来创建装饰器:
class CountCalls:def __init__(self, func):self.func = funcself.num_calls = 0def __call__(self, *args, **kwargs):self.num_calls += 1print(f"第 {self.num_calls} 次调用 {self.func.__name__}")return self.func(*args, **kwargs)@CountCalls
def say_hi():print("Hi!")say_hi() # 输出: 第 1 次调用 say_hi
say_hi() # 输出: 第 2 次调用 say_hi
常见应用场景
装饰器在实际开发中有很多用途:
- 日志记录:记录函数调用和参数
- 性能测试:测量函数执行时间
- 缓存:实现函数结果的缓存
- 权限验证:检查用户权限
- 重试机制:函数失败时自动重试
下面是一个记录函数执行时间的装饰器示例:
import time
import functoolsdef timer(func):@functools.wraps(func)def wrapper(*args, **kwargs):start_time = time.perf_counter()result = func(*args, **kwargs)end_time = time.perf_counter()print(f"函数 {func.__name__} 执行时间: {end_time - start_time:.4f} 秒")return resultreturn wrapper@timer
def calculate_sum(n):return sum(range(n + 1))result = calculate_sum(1000000)
print(f"结果: {result}")
通过这些示例,你应该能够理解装饰器的基本原理并开始手写自己的装饰器了。装饰器的核心是闭包和函数式编程的概念,掌握它们将大大提升你的Python
编程能力。
默认装饰器
Python
内置了多个实用的装饰器,它们广泛应用于日常编程中。以下是一些常用的默认装饰器及其应用场景:
1. @staticmethod
- 静态方法
- 作用:将类中的方法转换为静态方法,使其不依赖于类实例或类本身。
- 特点:
- 无需传入
self
或cls
参数。 - 无法访问类或实例的属性和方法。
- 无需传入
- 应用场景:与类相关但不依赖于类状态的工具函数。
class MathUtils:@staticmethoddef add(a, b):return a + b# 使用方式
print(MathUtils.add(3, 5)) # 无需创建实例
2. @classmethod
- 类方法
- 作用:将方法绑定到类而非实例,接收
cls
参数(类本身)。 - 特点:
- 可通过类或实例调用。
- 常用于创建工厂方法或修改类属性。
- 应用场景:工厂模式、替代构造函数。
class Person:def __init__(self, name, age):self.name = nameself.age = age@classmethoddef from_birth_year(cls, name, birth_year):age = 2023 - birth_yearreturn cls(name, age) # 使用cls创建实例# 使用方式
person = Person.from_birth_year("张三", 1990)
print(person.age) # 33
3. @property
- 属性装饰器
- 作用:将方法转换为只读属性,简化属性访问。
- 特点:
- 调用时无需括号(如
obj.prop
而非obj.prop()
)。 - 可与
@prop.setter
和@prop.deleter
组合使用,实现属性的修改和删除。
- 调用时无需括号(如
- 应用场景:计算属性、数据验证、属性访问控制。
class Circle:def __init__(self, radius):self._radius = radius@propertydef radius(self):return self._radius@radius.setterdef radius(self, value):if value < 0:raise ValueError("半径不能为负")self._radius = value@propertydef area(self):return 3.14 * self._radius ** 2# 使用方式
c = Circle(5)
print(c.area) # 78.5
c.radius = 10 # 自动触发setter验证
4. @functools.lru_cache
- 缓存装饰器
- 作用:缓存函数的返回值,避免重复计算,提升性能。
- 特点:
- 基于最近最少使用(
LRU
)算法。 - 适用于纯函数(输入相同则输出相同)。
- 基于最近最少使用(
- 应用场景:递归函数、耗时计算、API调用。
import functools@functools.lru_cache(maxsize=128)
def fibonacci(n):if n <= 1:return nreturn fibonacci(n-1) + fibonacci(n-2)# 使用方式
print(fibonacci(50)) # 秒级响应(缓存优化)
5. @functools.wraps
- 保留函数元信息
- 作用:在自定义装饰器中保留被装饰函数的元信息(如名称、文档字符串)。
- 特点:
- 避免装饰器覆盖原函数的
__name__
、__doc__
等属性。
- 避免装饰器覆盖原函数的
- 应用场景:自定义装饰器的开发。
import functoolsdef my_decorator(func):@functools.wraps(func) # 保留原函数元信息def wrapper(*args, **kwargs):return func(*args, **kwargs)return wrapper@my_decorator
def example():"""这是一个示例函数"""passprint(example.__name__) # 输出 "example",而非 "wrapper"
print(example.__doc__) # 输出 "这是一个示例函数"
6. @contextlib.contextmanager
- 上下文管理器
- 作用:将生成器函数转换为上下文管理器(支持
with
语句)。 - 特点:
- 通过
yield
分隔上下文的进入和退出逻辑。
- 通过
- 应用场景:资源管理、异常处理。
from contextlib import contextmanager@contextmanager
def open_file(filename, mode):f = open(filename, mode)try:yield f # 进入with语句时返回资源finally:f.close() # 退出with语句时释放资源# 使用方式
with open_file("test.txt", "w") as f:f.write("Hello, world!")
7. @dataclass
- 数据类装饰器(Python 3.7+)
- 作用:自动生成
__init__
、__repr__
、__eq__
等方法,简化数据类定义。 - 特点:
- 减少样板代码,提升开发效率。
- 应用场景:纯数据存储类。
from dataclasses import dataclass@dataclass
class Point:x: floaty: float# 自动生成__init__
p = Point(3, 4)
print(p) # 自动生成__repr__: Point(x=3, y=4)
8. @abc.abstractmethod
- 抽象方法(Python的抽象基类)
- 作用:定义抽象方法,强制子类实现该方法。
- 特点:
- 含抽象方法的类无法实例化,必须被子类继承并实现所有抽象方法。
- 应用场景:接口定义、框架设计。
from abc import ABC, abstractmethodclass Shape(ABC):@abstractmethoddef area(self):passclass Circle(Shape):def __init__(self, radius):self.radius = radiusdef area(self): # 必须实现抽象方法return 3.14 * self.radius ** 2
总结
这些内置装饰器覆盖了面向对象编程、性能优化、代码简洁性等多个方面。合理使用它们可以显著提升代码质量和开发效率。此外,Python
社区也提供了许多第三方装饰器(如Flask
的@app.route
、Django的@login_required
),进一步扩展了装饰器的应用场景。