Python类装饰器使用_元编程解析【教程】


类装饰器是实现__init__和__call__的类,通过元编程在装饰时初始化、调用时增强行为;可装饰函数(记录/缓存)或类(单例/实例控制),支持带参需三层嵌套。

Python类装饰器本质是利用__call__方法实现的可调用对象,它通过元编程思想在类定义阶段或实例化阶段动态干预行为——不是“给类加功能”,而是“让类本身成为装饰器”。关键在于理解:类装饰器 = 实现了__init____call__的类。

类装饰器的基本结构与执行时机

一个标准类装饰器必须满足两个条件:接收被装饰对象(函数或类)作为__init__参数,并在__call__中定义实际增强逻辑。注意:__call__只在被装饰对象**被调用时**触发(对函数装饰器),而类装饰器本身在**装饰语法糖执行时**就完成初始化。

  • @MyDecorator 出现时,会立即执行 MyDecorator(func) → 触发 __init__
  • 之后每次调用 func(),才触发 MyDecorator 实例.__call__()
  • 若装饰的是类(如 @MyDecorator 放在 class 前),__call__ 会在该类被实例化时运行(即 MyClass() 时)

函数级类装饰器:记录调用与缓存示例

这是最常见用法。例如写一个带计数器和简单缓存的装饰器:

class CountAndCache:
    def __init__(self, func):
        self.func = func
        self.count = 0
        self.cache = {}
        # 保留原函数元信息
        import functools
        functools.update_wrapper(self, func)
def __call__(self, *args):
    self.count += 1
    key = str(args)
    if key not in self.cache:
        self.cache[key] = self.func(*args)
    return self.cache[key]

@CountAndCache def add(a, b): print("计算中...") return a + b

调用 add(2, 3) 会打印一次“计算中...”,第二次相同参数则直接返回缓存值,同时 add.count 可查总调用次数。

类级类装饰器:控制实例化过程

当类装饰器用于修饰另一个类时,它能拦截并改造该类的实例化行为,典型场景包括单例、类型检查、自动注册等。

  • __init__ 接收的是被装饰的类(如 MyClass),不是实例
  • __call__MyClass() 被调用时执行,可决定是否新建实例、返回代理、抛出异常等
  • 需返回一个可替代原类行为的对象(通常是新实例,或封装后的代理)

例如单例实现:

class Singleton:
    def __init__(self, cls):
        self.cls = cls
        self.instance = None
def __call__(self, *args, **kwargs):
    if self.instance is None:
        self.instance = self.cls(*args, **kwargs)
    return self.instance

@Singleton class Database: def init(self): print("数据库连接已创建")

连续执行 db1 = Database()db2 = Database(),只会打印一次“数据库连接已创建”,且 db1 is db2True

进阶:支持带参数的类装饰器

要实现 @MyDecorator(arg=10) 这种带参形式,需再包一层工厂类或函数。推荐用类实现三层嵌套:

  • 外层类(如 RetryOnFailure):接收装饰器参数,只实现 __init__
  • 中间 __call__:接收被装饰对象(函数),返回一个内部装饰器实例
  • 内层类(如 Wrapper):真正保存函数并实现逻辑,含自己的 __call__

简明写法(省略细节):

class RetryOnFailure:
    def __init__(self, max_retries=3):
        self.max_retries = max_retries
def __call__(self, func):
    class Wrapper:
        def __init__(self, f):
            self.func = f
            import functools
            functools.update_wrapper(self, f)
        def __call__(self, *args, **kwargs):
            for i in range(self.max_retries):
                try:
                    return self.func(*args, **kwargs)
                except Exception:
                    if i == self.max_retries - 1:
                        raise
    return Wrapper(func)

这样就能用 @RetryOnFailure(max_retries=5) 灵活配置了。

不复杂但容易忽略:类装饰器的__call__返回值,就是被装饰对象对外表现的行为;所有魔法方法(如__get__)若涉及描述符协议,也需手动处理才能保持函数属性完整。


# python  # app  # ai 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 网络优化76771 】 【 技术知识130152 】 【 IDC云计算60162 】 【 营销推广131313 】 【 AI优化88182 】 【 百度推广37138 】 【 网站推荐60173 】 【 精选阅读31334


相关推荐: Mac如何将HEIC图片格式转为JPG_Mac批量转换图片【指南】  如何高效识别并拦截拼接式恶意域名 spam  Win10怎么卸载鲁大师_Win10彻底卸载鲁大师方法【步骤】  Python与Docker容器化部署实战_镜像构建与CI/CD流程  如何在Golang中处理二进制数据_Golang io与encoding/binary二进制操作方法  Python随机数生成_random模块说明【指导】  如何用正则与预处理高效拦截带干扰符的恶意域名  php485读数据时阻塞怎么办_php485非阻塞读取设置技巧【详解】  Windows10如何更改鼠标图标_Win10鼠标属性指针浏览  PowerShell怎么创建复杂的XML结构  PHP主流架构怎么监控运行状态_工具推荐【操作】  Win11怎么关闭自动修复_跳过Win11开机自动修复循环【技巧】  Django 测试数据库表缺失与字段未创建问题的完整解决方案  php中常量能用::访问吗_类常量与作用域操作符使用场景【汇总】  c++如何用AFL++进行模糊测试 c++ Fuzzing入门【安全】  Python对象比较与排序_魔术方法解析【教程】  如何从 Go 的 map[string]interface{} 中安全获取值  Windows 11无法安全删除U盘提示设备正在使用中怎么办_Windows 11找出占用设备进程  c++怎么处理多线程死锁_c++ lock_guard与unique_lock锁管理【技巧】  Win11怎么开启自动HDR画质_Windows11显示设置HDR选项  php8.4xdebug无法调试怎么办_php8.4xdebug配置问题解决【解答】  mac本地php环境如何开启curl_curl扩展启用与测试步骤详解【汇总】  php做exe支持多线程吗_并发处理实现方式【详解】  如何在 Go 中正确反序列化 XML 多节点数组(解决仅解析首个元素的问题)  MAC如何安装Git版本控制工具_MAC开发环境配置与Xcode插件安装【教程】  Win11怎么开启智能存储_Windows11存储感知自动清理文件  PHP 中 require() 语句返回值的用法详解  php报错怎么查看_定位PHP致命错误与警告的方法【教程】  Win11怎么更改鼠标指针_Windows 11自定义鼠标样式与大小【美化】  Win11怎么设置环境变量_Win11配置Path路径变量【详解】  Python对象比较排序规则_集合使用说明【指导】  Windows 11怎么设置默认解压软件_Windows 11为ZIP/RAR文件指定默认打开程序  Win11 C盘满了怎么清理 Win11磁盘清理和存储感知使用教程【新手必看】  Python与GPU加速技术_CUDA与Numba高性能计算实践  如何在Golang中写入JSON文件_保存结构体数据到文件  Win10怎样卸载TeamViewer_Win10卸载TeamViewer步骤【教程】  静态属性修改会影响所有实例吗_php作用域操作符下静态存储【教程】  如何在Golang中处理通道发送接收错误_防止阻塞或panic  如何解决Windows时间不准的问题?(自动同步设置)  为什么Go需要go mod文件_Go go mod文件作用说明  Win11怎么快速锁屏_Win11一键锁屏快捷键Win+L【基础】  php订单日志怎么按金额排序_php按订单金额排序日志方法【方法】  为什么Go建议使用error接口作为错误返回_Go Error接口设计原因说明  如何使用Golang reflect检查方法数量_动态分析类型方法  Go 语言标准库为何不提供泛型 Contains 方法:设计哲学与类型系统约束  Windows 10怎么隐藏特定更新补丁_Windows 10使用微软官方工具wushowhide.diagcab  LINUX如何删除用户和用户组_Linux userdel和groupdel命令用法【系统管理】  Mac的“预览”如何合并多个PDF_Mac文件处理技巧【效率】  Python大文件处理策略_内存优化说明【指导】  Python生成器表达式内存优化_惰性计算说明【指导】 

 2026-01-01

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

致胜网络推广营销网


致胜网络推广营销网

致胜网络推广营销网专注海外推广十年,是谷歌推广.Facebook广告全球合作伙伴,我们精英化的技术团队为企业提供谷歌海外推广+外贸网站建设+网站维护运营+Google SEO优化+社交营销为您提供一站式海外营销服务。

 915688610

 17370845950

 915688610@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.