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

深入掌握 Python 面向对象的灵魂——魔法函数(Magic / Dunder Methods)全景指南

目录

一、为什么是“魔法”

二、魔法函数的分类总览

三、对象生命周期

四、对象描述

五、属性管理

六、容器协议

七、可调用对象与 with 语句

八、数值/位运算

九、比较与排序

十、异步魔法函数(Python 3.5+)

 十一、结语


一、为什么是“魔法”

 在 Python 里,所有以双下划线开头、又以双下划线结尾的标识符,都被解释器预留为特殊用途。它们数量众多、各司其职,却又能让自定义类无缝融入 Python 的原生语法与执行模型。因为它们的存在,len(obj) 可以工作,for i in obj 可以遍历,with obj 可以自动管理资源,甚至连 +、[]、== 这些看似“内置”的运算,都能被用户类重新定义。于是,社区把这种“看似黑箱、实则透明”的机制称为 Magic Methods,中文通常叫“魔法函数”或“魔术方法”。

二、魔法函数的分类总览

 官方文档把它们散布在 data-model 章节,初学者常感零散。若按功能归纳,可分为七大类:

  1. 对象生命周期:创建、初始化、析构

  2. 对象描述:字符串表示、字节表示、哈希、布尔值

  3. 属性管理:点号访问、描述符协议

  4. 容器协议:长度、取值、赋值、删除、迭代

  5. 可调用与上下文:把对象当函数、进入/退出 with

  6. 数值/位运算:一元、二元、原地、反向运算

  7. 比较与富比较:==、>、<、排序键等

下面逐类展开,给出最小可运行示例与工程化建议。

三、对象生命周期

1.new(cls, *args, **kw)
负责“生”,是真正的构造器;必须返回一个实例对象。

class Singleton:_inst = Nonedef __new__(cls, *a, **k):if cls._inst is None:cls._inst = super().__new__(cls)return cls._inst

注意:new 常用于不可变类型子类化(如 tuple、int)或控制实例创建策略;日常业务代码极少需要重写。

2.init(self, …)
负责“养”,在 new 返回的实例上初始化属性。若父类有 init,记得 super().init()。

3.del(self)
当对象的引用计数归零、即将被回收时调用。由于 Python 的垃圾回收策略不确定,del 里不要访问全局资源,也不要依赖执行时机。更安全的做法是实现上下文管理器(见第 5 节)。

四、对象描述

1.字符串表示

repr(self):给开发者看的“官方”字符串,理想状态下 eval(repr(obj))==obj。

str(self):给最终用户看的友好文本。

format(self, spec):支持 f'{obj:spec}'。

bytes(self):定义 bytes(obj)。

2.布尔值与哈希

bool(self):控制 bool(obj)。若未定义,则退回到 len;若都无,默认真。

hash(self):让对象可放入 set/dict;必须与 eq 保持一致性:a==b ⇒ hash(a)==hash(b)。

五、属性管理

1.点号访问三兄弟

getattr(self, name):访问不存在的属性时触发。

setattr(self, name, value):任何属性赋值都会触发,因此内部赋值需用 super().

setattr 或 self.

dict 避免递归。

delattr(self, name):删除属性。

2.描述符协议(descriptor protocol)

get(self, obj, owner) / set / delete
这是实现 @property、staticmethod、classmethod 的底层机制。写 ORM 字段、类型校验库时常用。

示例:自定义验证字段

class Range:def __init__(self, min_, max_):self.min, self.max = min_, max_def __set_name__(self, owner, name):self.attr = namedef __set__(self, obj, value):if not self.min <= value <= self.max:raise ValueError(value)obj.__dict__[self.attr] = valuedef __get__(self, obj, owner):return obj.__dict__[self.attr]class Person:age = Range(0, 120)

六、容器协议

要让自定义类表现得像列表、字典,只需实现以下若干魔法函数: 

  • len(self) → 支持 len(obj)

  • getitem(self, key) → obj[key];key 可为整数或 slice 对象

  • setitem(self, key, value) → obj[key]=value

  • delitem(self, key) → del obj[key]

  • iter(self) → iter(obj)

  • reversed(self) → reversed(obj)

  • contains(self, item) → item in obj

示例:实现一个只读平方表

class SquareTable:def __init__(self, n):self._n = ndef __len__(self):          return self._ndef __getitem__(self, idx):if 0 <= idx < self._n:return idx * idxraise IndexErrordef __iter__(self):for i in range(self._n):yield i * i

实现了上述函数后,for x in SquareTable(5)、list(SquareTable(3))、2 in SquareTable(10) 都能直接工作,这就是协议的力量。

七、可调用对象与 with 语句

1.call(self, *a, **k)
把实例当函数用,常用于策略模式、缓存装饰器、闭包替代。

class Counter:def __init__(self):self.n = 0def __call__(self):self.n += 1return self.n
c = Counter()
print(c(), c())   # 1 2

2.上下文管理器协议

  • enter(self) → 返回值赋给 as 变量

  • exit(self, exc_type, exc, tb) → 清理资源,返回 True 可吞掉异常

class temp_chdir:def __init__(self, path):self.path = pathself.origin = Nonedef __enter__(self):import osself.origin = os.getcwd()os.chdir(self.path)return self.pathdef __exit__(self, *args):import osos.chdir(self.origin)with temp_chdir('/tmp'):open('test.txt', 'w').write('hello')

标准库 contextlib.contextmanager 也依赖此协议。

八、数值/位运算

 Python 将运算符全部映射到魔法函数,例如:

  • add

  • sub

  • mul
    @ → matmul
    / → truediv
    // → floordiv
    % → mod
    ** → pow
    & → and
    | → or
    ^ → xor
    << → lshift

rshift

每个运算符又有“正向”、“反向”、“就地”三个版本:
正向:add(self, other)
反向:radd(self, other)
就地:iadd(self, other)

反向版本用于解决 int + Vector 时,int 无法识别 Vector 的场景;就地版本则用于 += 运算符,若未定义则退化为正向运算后赋值。

示例:二维向量

class V2:def __init__(self, x, y):self.x, self.y = x, ydef __repr__(self): return f'V2({self.x}, {self.y})'def __add__(self, other):if isinstance(other, V2):return V2(self.x + other.x, self.y + other.y)return NotImplementeddef __radd__(self, other):  # 交换律可复用return self + otherdef __iadd__(self, other):if isinstance(other, V2):self.x += other.xself.y += other.yreturn selfreturn NotImplemented

返回 NotImplemented 能触发解释器尝试 other 的反向运算,保持可扩展性。

九、比较与排序

  • eqneltlegtge

  • hash(若对象不可变,通常同时实现,以便作为 dict 键)

  • bool(见第四节)

functools.total_ordering 装饰器能根据 eq 与任一排序魔法函数自动生成剩余方法,减少样板代码。

十、异步魔法函数(Python 3.5+)

  • await:定义 await obj

  • aiteranext:异步迭代器

  • aenteraexit:异步上下文管理器

示例:异步计数器

class AsyncCounter:def __init__(self, n):self.i = 0self.n = ndef __aiter__(self):return selfasync def __anext__(self):if self.i >= self.n:raise StopAsyncIterationawait asyncio.sleep(0.1)self.i += 1return self.i

 十一、结语

 魔法函数不是炫技,而是 Python 设计哲学的浓缩:
“不强迫继承,只要求协议。”
当你熟练掌握了 getitemiterenteradd 等双下方法,就能写出“像内置一样自然”的类库:Requests 让网络请求像字典,Pydantic 让数据校验像 dataclass,FastAPI 让 Web 路由像函数。它们背后,正是魔法函数在默默工作。
希望本文的梳理与示例,能让你在面向对象的道路上,真正拥有“让代码说话”的魔法力量。

http://www.lryc.cn/news/597332.html

相关文章:

  • CAN的终端电阻
  • 设计模式代码总结
  • 用 PyTorch 实现全连接网络识别 MNIST 手写数字
  • Android插件化实现方案深度分析
  • window下c++共享内存,进程互斥锁。
  • macOS配置maven及报错处理:zsh: permission denied: mvn
  • 大厂总结常用分析问题方法之CMMI-IDEAL模型
  • VRRP技术-设备备份技术
  • Modbus TCP转Devicenet:水泥厂PLC与多类仪表的自动化通信实践
  • 学习 Flutter(五):玩安卓项目实战 - 下
  • 2025年7月一区SCI-投影迭代优化算法Projection Iterative Methods-附Matlab免费代码
  • Flutter学习笔记(四)---基础Widget
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘jupyter’问题
  • OSPF路由协议——上
  • 2025.7.15vlan作业
  • vscode怎么安装MINGW
  • Linux下SVN常用指令
  • VRRP虚拟路由器冗余协议
  • 民营医院如何突破技术与模式创新,迎来发展机遇?
  • 14.10 《24小时单卡训练!LoRA微调LLaMA2-7B全攻略,RTX 3090轻松跑》
  • Async/Await
  • translateZ数值大小变化
  • Python 程序设计讲义(7):Python 的基本数据类型——整数类型
  • SpringMVC快速入门之请求与响应
  • JavaScript事件循环机制
  • 免费下载入户申请书,轻松办理登记手续——“文件扫描助手”网站介绍
  • 使用 piano_transcription_inference将钢琴录音转换为 MIDI
  • 开闭原则在C++中的实现
  • 基于Tornado的WebSocket实时聊天系统:从零到一构建与解析
  • 【js(5)原型与原型链】