装饰器工厂与类装饰器:进阶装饰器技术解析

news/2025/10/11 1:22:22/文章来源:https://www.cnblogs.com/wangya216/p/19134138

装饰器工厂与类装饰器:进阶装饰器技术解析

装饰器是Python中强大的元编程工具,除多种进阶形式。其中装饰器工厂(Decorator Factory)和类装饰器(Class Decorator)是两种重要变体,分别解决“带参数的装饰器”和“复杂状态管理的装饰器”问题。下面详细解析两者的原理、用法和区别。

一、装饰器工厂:带参数的装饰器生成器

装饰器工厂本质是“生成装饰器的函数”,它接受参数并返回一个装饰器(通常是函数装饰器),用于解决“装饰器需要自定义参数”的场景(如控制装饰器的行为细节)。

1. 为什么需要装饰器工厂?

普通装饰器(如之前的计时装饰器)无法直接接收参数,若想让装饰器更灵活(例如控制日志格式、设置超时时间),就需要用装饰器工厂“动态生成”带有参数的装饰器。

例如:希望计时装饰器能接收一个参数 unit,控制输出时间的单位(秒/毫秒)。

2. 装饰器工厂的实现原理

装饰器工厂的结构是“函数嵌套三层”

  • 外层函数:装饰器工厂,接收装饰器的参数;
  • 中层函数:真正的装饰器,接收被装饰的函数;
  • 内层函数:闭包(wrapper),包裹原函数执行逻辑,使用外层的参数。
# 装饰器工厂:接收参数,返回装饰器
def timer_factory(unit="second"):  # 外层:装饰器工厂,接收参数 unit# 中层:真正的装饰器,接收被装饰函数def timer_decorator(func):# 内层:闭包,使用外层的 unit 参数def wrapper(*args, **kwargs):import timestart = time.time()result = func(*args, **kwargs)end = time.time()duration = end - start# 根据 unit 参数格式化输出if unit == "ms":print(f"{func.__name__} 耗时:{duration*1000:.2f} 毫秒")else:print(f"{func.__name__} 耗时:{duration:.2f} 秒")return resultreturn wrapper  # 返回闭包return timer_decorator  # 返回装饰器# 使用装饰器工厂:先传参数生成装饰器,再装饰函数
@timer_factory(unit="ms")  # 等价于:@生成的 timer_decorator
def slow_function(n):import timetime.sleep(n)return nslow_function(1)  # 输出:slow_function 耗时:1001.23 毫秒(单位由参数控制)

3. 执行流程解析

  1. 调用 timer_factory(unit="ms"),返回一个定制化的装饰器 timer_decorator(该装饰器“记住”了 unit="ms" 这个参数);
  2. @timer_decorator 作用于 slow_function,即 slow_function = timer_decorator(slow_function)
  3. 调用 slow_function(1) 时,执行闭包 wrapper,使用 unit="ms" 格式化输出。

二、类装饰器:基于类的装饰器实现

类装饰器是用类实现的装饰器,通过类的特殊方法(__init____call__)实现装饰逻辑,尤其适合需要管理复杂状态(如计数、缓存)的场景。

1. 类装饰器的核心要素

要实现类装饰器,需满足:

  • __init__ 方法:接收被装饰的函数作为参数,保存原函数引用(类似函数装饰器接收原函数);
  • __call__ 方法:实现装饰逻辑,包裹原函数执行(类似闭包 wrapper 的作用),可通过实例属性保存状态。

2. 基础示例:带状态的类装饰器

下面实现一个统计函数调用次数的类装饰器,展示其状态管理能力:

class CallCounter:# 初始化:接收被装饰函数,初始化状态(计数)def __init__(self, func):self.func = func  # 保存原函数self.count = 0    # 实例属性:记录调用次数(状态持久化)# 调用:实现装饰逻辑,每次调用计数+1def __call__(self, *args, **kwargs):self.count += 1print(f"[{self.func.__name__}] 第 {self.count} 次调用")return self.func(*args, **kwargs)  # 调用原函数# 使用类装饰器
@CallCounter
def add(a, b):return a + badd(1, 2)  # 输出:[add] 第 1 次调用 → 返回 3
add(3, 4)  # 输出:[add] 第 2 次调用 → 返回 7

优势:实例属性 self.count 自然支持状态持久化,无需像函数装饰器那样使用 nonlocal 关键字。

3. 带参数的类装饰器

类装饰器也可以通过“类工厂”的方式支持参数(类似装饰器工厂的思路),即先定义一个接收参数的类,再返回一个装饰器类。

例如,让计数装饰器支持“自定义前缀文本”:

# 类工厂:接收参数,返回定制化的类装饰器
def counter_factory(prefix="调用次数"):class CustomCallCounter:def __init__(self, func):self.func = funcself.count = 0self.prefix = prefix  # 使用工厂参数def __call__(self, *args, **kwargs):self.count += 1print(f"[{self.func.__name__}] {self.prefix}:{self.count}")return self.func(*args, **kwargs)return CustomCallCounter  # 返回定制化的类装饰器# 使用带参数的类装饰器
@counter_factory(prefix="第 N 次执行")
def multiply(a, b):return a * bmultiply(2, 3)  # 输出:[multiply] 第 N 次执行:1 → 返回 6
multiply(4, 5)  # 输出:[multiply] 第 N 次执行:2 → 返回 20

三、装饰器工厂与类装饰器的对比

维度 装饰器工厂(函数式) 类装饰器(基于类)
核心结构 三层函数嵌套(工厂→装饰器→闭包) 类 + __init__ + __call__ 方法
状态管理 依赖闭包的非局部变量(nonlocal 依赖实例属性(self.xxx),更直观
支持参数 原生支持(外层函数接收参数) 需要通过“类工厂”间接支持
适用场景 轻量参数定制(如格式、开关) 复杂状态管理(如计数、缓存、权限)
可读性 简单场景清晰,复杂场景嵌套过深 结构清晰,适合扩展(如继承)

四、总结

-** 装饰器工厂:是“生成装饰器的函数”,通过三层嵌套实现带参数的装饰器,适合需要灵活定制装饰逻辑的场景(如控制输出格式)。
-
类装饰器**:用类实现的装饰器,通过 __init____call__ 方法工作,优势在于状态管理(实例属性自然保存状态),适合复杂逻辑(如计数、缓存)。

两者都是装饰器模式的进阶形式,选择哪种取决于需求:简单参数定制用装饰器工厂,复杂状态管理用类装饰器。理解它们的原理,能让你在实际开发中更灵活地使用装饰器解决问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/934503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

53最大子数组和 动态规划和分制 - MKT

53最大子数组和 动态规划和分制 class Solution { public:// 时间不通过int maxSubArray_2(vector<int>& nums) {int tager_max=nums[0];int left=0;// sum_[i] map<int,int> sum_i; // 和 索引 由于…

Codeforces 2153D Not Alone 题解 [ 绿 ] [ 线性 DP ] [ 分类讨论 ]

Not Alone:唐题。 容易将题意转化为:环上的每一个颜色段长度都 \(\ge 2\),求最小操作数。 再考虑一个 \(O(n^2\log n)\) 的暴力,定义 \(dp_i\) 表示以 \(i\) 结尾的最小操作数,然后枚举前一个转移点 \(j\)。继续考…

__closure__:闭包的“身份证”

要理解“闭包函数和嵌套函数的区别在于 __closure__ 属性”,首先需要明确 嵌套函数 和 闭包函数 的基础关系——闭包函数是“满足特定条件的嵌套函数”,而 __closure__ 属性正是闭包函数区别于普通嵌套函数的“标志性…

Codeforces Round 1057 (Div. 2)

A. Circle of Apple Trees 题意:一个环形数组,每次到一个位置可以选择拿走这个元素或者跳过,然后走到下一个位置。每次拿的数要比之前拿的大,求最多拿多少数。 显然可以从小到大拿,那么答案就是不同数的个数。点击…

“表达式”(Expression)和“语句”(Statement)概念辨析

在编程中,“表达式”(Expression)和“语句”(Statement)是两个基础且容易混淆的概念。它们的核心区别在于是否有返回值以及在代码中的作用,以下从定义、特点、示例三个维度详细说明: 一、定义与核心区别表达式(…

每日一题 ###121买卖股票的最佳时机

仅以此记录所学所想,如有错误,还望指正。 首次尝试 1、我小小的脑子只能想出暴力解法,结果是超时了。 class Solution { public:int maxProfit(vector<int>& prices) {int max=0;for(int i=0;i<prices…

10.10总结

1.将一个数组中的值按逆序重新存放进行了编程联系,生成了对应程序 2.明天学习完善程序细节 3.今天收获满满

LibreChat-图文并茂手把手教你界面配置 | Adorable LibreChat Interface Configuration Guide

@目录🎀 整体介绍 | Overview🍪 配置项详解 | Field Details🎨 开关大全 | All Switches✨ 配置示例 | Cute ExampleConclusion | 结语 🎀 整体介绍 | Overview 今天我们来学习一下,LibreChat的页面基础配置哇…

GAE-广义优势估计算法介绍

一句话总结 GAE 就像「既要稳又要准」的聪明妥协:用多步 TD 误差加权平均,既缓解了 MC 的高方差,又减少了 TD 的单一偏差,通过调节参数(λ)灵活平衡两者的优缺点。MC vs TD 的痛点MC(蒙特卡洛):原理:跑完整个…

qemu模拟单片机

ESP32 乐鑫官方移植的qemu工程:espressif/qemu: Fork of QEMU with Espressif patches. See Wiki for details. 官方使用说明:esp-toolchain-docs/qemu/README.md at main espressif/esp-toolchain-docs 目前支持ES…

RAG-检索增强生成

检索增强生成(Retrieval-Augmented Generation, RAG)是近年来深度学习领域的重要技术突破,旨在解决大语言模型(LLM)在知识更新、事实准确性以及领域专业化方面的局限性。本文将从核心架构、功能实现、实践规范到高…

“猴子补丁”(monkey patch)跟猴子有关吗?

“猴子补丁”(monkey patch)指的是使函数名指向另一个函数。 这个名字里的“猴子”(monkey)和真实的动物猴子没有任何生物学或实体关联,它是编程领域的一个形象比喻,名字的由来与“猴子”的俚语含义有关。 为什么…

Yapi 使用docker在cenos7上部署教程与基本使用

✅ 前提条件 确保你已经完成以下准备:CentOS 7 系统已安装 Docker(未安装请先安装)拥有管理员权限(root 或 sudo)1、安装mongo数据库# 拉取mongo镜像,有本地镜像包可以直接导入 docker pull mongo # 安装mongo数…

C语言vsC++

一、核心区别设计理念C 语言:面向过程(Procedure-Oriented),以函数和数据结构为核心,强调算法和步骤的实现,关注 “怎么做”。 C++:基于 C 语言扩展而来,支持面向对象编程(Object-Oriented,OO),引入类、对…

20251010 之所思 - 人生如梦

20251010 之所思这两天想的事情有点多,有点焦虑,因为刚刚升职,部门的软件需要管理起来,已经不再仅仅管理自己现在的一个小组,今天看了一下软件的人数,数量已经到了122人,这是一个很庞大的队伍,如何管理好,心里…

2025.10.10

今天学生会开会导致十点没有回到宿舍 今天一节英语课,单词背诵情况不好,以后会加强 尝试轻食餐,不好吃

个人书单-从心流出发,学习积极心理学

从“心流”出发,构建你的积极心理学知识体系 | 专题书单 你是否曾有过这样的体验:在全神贯注地工作、运动或创作时,忘记了时间的流逝,内心充满了一种纯净而高效的愉悦感? 这就是米哈里契克森米哈赖所描述的 “心流…

等号(=)在C语言和python中有什么区别?

等号(=)在C语言和python中有什么区别? 等号(=)在 C 语言和 Python 中虽然核心功能都是“赋值”,但由于两种语言的设计理念(静态类型 vs 动态类型、编译型 vs 解释型)存在根本差异,= 的行为、特性和使用场景有…

AI元人文(十四)之价值共生篇:再论物物交换——作为价值共生基础的元协议

AI元人文(十四)之价值共生篇:再论物物交换——作为价值共生基础的元协议 文 / 岐金兰 摘要: 本文是对前文《AI元人文(十四):物物交换——论跨领域价值协商的元协议》提出的“物物交换”元协议的深化探讨。文章将…

4.布局系统

Aquarius 开发框架旨在帮助独立开发者和中小型团队,完成 iOS App 的快速实现与迭代。使用框架开发将给你带来简单、高效、易维护的编程体验。大家好,我是K哥。一名独立开发者,同时也是Swift开发框架【Aquarius】的作…