当前位置: 首页 > ai >正文

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
常见应用场景

装饰器在实际开发中有很多用途:

  1. 日志记录:记录函数调用和参数
  2. 性能测试:测量函数执行时间
  3. 缓存:实现函数结果的缓存
  4. 权限验证:检查用户权限
  5. 重试机制:函数失败时自动重试

下面是一个记录函数执行时间的装饰器示例:

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 - 静态方法
  • 作用:将类中的方法转换为静态方法,使其不依赖于类实例或类本身。
  • 特点
    • 无需传入 selfcls 参数。
    • 无法访问类或实例的属性和方法。
  • 应用场景:与类相关但不依赖于类状态的工具函数。
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),进一步扩展了装饰器的应用场景。

http://www.xdnf.cn/news/13970.html

相关文章:

  • 网络流量分析之Heavy hitters和heavy changers
  • Jmeter本身耗资源导致压测不上去解决方案
  • Java中extends与implements深度解析:继承与接口实现的本质区别
  • 【Docker基础】Docker核心概念:仓库(Registry)详解
  • Vue实现悬浮图片弹出大图预览弹窗,弹窗顶部与图片顶部平齐
  • 隧道代理IP的使用与技术优势分析
  • 几种经典排序算法的C++实现
  • jenkins连接git仓库
  • 03 - ECA模块
  • git checkout 详解
  • 危险品运输行业观察
  • Kafka环境搭建全攻略:从Docker到Java实战
  • Logback-spring.xml 配置屏蔽特定路径的日志
  • Vue3+Element Plus动态表格列宽设置
  • 【写实交互数字人】实时数字人助力政务变革:技术、产品与应用价值的全景剖析
  • 【插件推荐】WebRTC Protect — 防止 IP 泄漏
  • 苹果越来越像安卓,华为越来越像苹果
  • 电路图识图基础知识-电动机软启动器技术解析与应用(二十五)
  • 【Zephyr 系列 22】从单机开发到平台化:构建你自己的 Zephyr 物联网开发平台
  • 【结合JSR380自定义校验】
  • Altera系列FPGA基于ADV7180解码PAL视频,纯verilog去隔行,提供2套Quartus工程源码和技术支持
  • 智慧物流园区——解读华为智慧物流园区解决方案【附全文阅读】
  • 上海市计算机学会竞赛平台2022年4月月赛丙组圆环独立集(一)
  • 基于 Spring Cloud Gateway + Sentinel 实现高并发限流保护机制
  • PHP基础-控制结构
  • 全链路实时感知:网络专线端到端监控运维
  • SwiftUI隐藏返回按钮保留右滑手势方案
  • MyBatis原理
  • 关于阿里云-云消息队列MQTT的连接和使用,以及SpringBoot的集成使用
  • P8784 [蓝桥杯 2022 省 B] 积木画