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


相关推荐: c++怎么操作redis数据库_c++ hiredis库连接与命令执行【实战】  Win11怎么硬盘分区 Win11新建磁盘分区详细教程【步骤】  Win11怎么解压RAR文件 Win11自带解压功能使用方法  如何使用Golang开发基础文件下载功能_Golang HTTP文件响应与缓存实现  Win11怎么激活Windows10_Win11激活Win10系统方法【步骤】  手机php怎么转mp4_手机端php文件转mp4app推荐【指南】  php增删改查需要哪些扩展_开启mysqli或pdo扩展方法【说明】  如何在JavaScript中动态拼接PHP的base_url与前端变量  Windows执行文件被SmartScreen拦截原因_安全提示与绕过方式  Win11怎样安装微信开发者工具_Win11安装开发者工具教程【步骤】  如何提升Golang程序I/O性能_Golang I/O密集型程序优化示例  Windows10电脑怎么设置文件权限_Win10安全选项卡所有者修改  Bpmn 2.0的XML文件怎么画流程图  Win11怎么清理C盘下载文件夹_Win11清理下载文件夹技巧【教程】  Win11怎么设置应用分屏_Windows11贴靠布局Snap Layouts  php内存溢出怎么排查_php内存限制调试与优化方法【说明】  如何在Golang中使用encoding/gob序列化对象_存储和传输数据  如何使用Golang构建基础消息队列模拟_Golang消息发送与消费实现方法  如何用正则表达式精确匹配最多含一个换行符的起止片段  如何外贸网站设计-能留住客户提升用户体验!  短链接还原php提示内存不足_调整PHP内存限制设置【技巧】  为什么本地php环境运行php脚本卡顿_php执行效率优化方法与设置【说明】  Dapper的Execute方法的返回值是什么意思 Dapper Execute返回值详解  Go 语言标准库为何不提供泛型切片的 Contains 方法?  c++怎么实现大文件的分块读写_c++ 文件指针seekp与seekg偏移控制【方法】  PHP主流架构怎么集成Redis缓存_配置步骤【方法】  php高频调试功能有哪些_php常用调试函数与工具汇总【解答】  Windows11怎样开启游戏模式_Windows11游戏模式开启攻略【方法】  Python网络异常模拟_测试说明【指导】  Win10如何更改电脑休眠时间_Windows10电源和睡眠选项调整  ACF 教程:如何正确更新嵌套在多层 Group 字段内的子字段  Golang如何遍历目录文件_Golang filepath.Walk目录遍历操作方法  如何使用Golang实现错误包装与传递_Golangfmt.Errorf%w使用实践  Win11怎么关闭系统透明度_Windows11个性化颜色透明效果  如何在 Django 中安全修改用户密码而不使会话失效  XML的“混合内容”是什么 怎么用DTD或XSD定义  c# 如何用c#实现一个支持优先级的任务队列  Win11怎么更改盘符_Win11磁盘管理修改驱动器号【步骤】  Win11任务栏颜色怎么改_Win11自定义任务栏配色设置【美化】  英国搜索:多数英国人认为语言搜索是未来搜索  Win11怎么更改任务栏颜色_Windows11个性化重音色设置  Mac怎么设置登录项_Mac管理开机自启动程序【教程】  网站体验不好=浪费钱:如何提升-用户体验效果差  MAC如何快速搜索大文件_MAC磁盘空间分析与冗余数据清理【方法】  Win11怎么清理C盘系统错误报告_Win11清理系统错误报告技巧【教程】  Win11用户账户控制怎么关_Win11关闭UAC弹窗提示【设置】  Windows怎样拦截QQ浏览器广告_Windows拦截QQ浏览器广告方法【方法】  Python网页解析流程_html结构说明【指导】  Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区  Windows10如何查看保存的WiFi密码_Win10命令行netsh wlan查询 

 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.