python

Python 魔法方法(Magic Methods)完全指南

一、概述

1. 定义

魔法方法(Magic Methods)是 Python 中形如 __xxx__(前后各两个下划线)的特殊方法,由 Python 解释器自动调用(无需手动触发),是 Python 面向对象编程的核心特性之一。

2. 核心作用

  • 给自定义类赋予「Python 内置类型行为」(如支持加减运算、切片、打印、迭代等);
  • 简化代码逻辑,让自定义类使用起来更符合 Python 语法习惯(Pythonic);
  • 实现底层功能封装(如实例创建、资源管理、哈希计算等)。

3. 调用机制

Python 解释器在执行特定操作时,会自动触发对应的魔法方法。例如:

  • 执行 a + b 时,触发 a.__add__(b)
  • 执行 print(a) 时,触发 a.__str__()
  • 执行 len(a) 时,触发 a.__len__()

二、常用魔法方法分类与详解

(一)构造与初始化:控制实例的创建、初始化与销毁

用于管理实例的生命周期,是自定义类最基础的魔法方法。

魔法方法 触发场景 核心作用 示例代码
__new__(cls, *args, **kwargs) 实例创建(在 __init__ 之前调用) 控制实例创建过程(如单例模式、限制实例数量) python class Singleton: _instance = None def __new__(cls): if not cls._instance: cls._instance = super().__new__(cls) return cls._instance
__init__(self, *args, **kwargs) 实例初始化(创建实例后自动调用) 给实例绑定属性、初始化参数 python class Person: def __init__(self, name, age): self.name = name self.age = age # 使用:p = Person("张三", 25)
__del__(self) 实例销毁(垃圾回收时调用) 释放资源(如关闭文件、断开数据库连接) python class FileHandler: def __init__(self, path): self.file = open(path, "w") def __del__(self): self.file.close() # 垃圾回收时自动关闭文件

关键说明:

  • __new__ 是「创建实例」的方法,返回实例对象;__init__ 是「初始化实例」的方法,不返回值,仅给实例赋值;
  • __del__ 不推荐过度依赖(垃圾回收时机不确定),释放资源建议优先使用上下文管理器(__enter__/__exit__)。

(二)字符串表示:控制实例的打印与转换格式

用于定义实例的字符串输出格式,让打印、转换时的结果更易读(面向用户或开发者)。

魔法方法 触发场景 核心作用 示例代码
__str__(self) print(self)str(self) 时触发 面向用户的友好字符串表示 python class Person: def __str__(self): return f"Person(name='{self.name}', age={self.age})" # 使用:print(p) → Person(name='张三', age=25)
__repr__(self) 终端直接输入实例、repr(self) 时触发 面向开发者的精确字符串表示(可还原实例) python class Person: def __repr__(self): return f"Person('{self.name}', {self.age})" # 使用:终端输入 p → Person('张三', 25)(eval(repr(p)) 可重建实例)
__format__(self, fmt_spec) format(self, fmt_spec) 时触发 自定义格式化字符串输出 python class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day def __format__(self, fmt): if fmt == "ymd": return f"{self.year}-{self.month}-{self.day}" elif fmt == "mdy": return f"{self.month}/{self.day}/{self.year}" return f"{self.year}.{self.month}.{self.day}" # 使用:format(Date(2024, 5, 20), "ymd") → "2024-05-20"

关键说明:

  • 若未实现 __str__,Python 会自动调用 __repr__ 替代;
  • __repr__ 的设计目标是「让输出可还原实例」,__str__ 的设计目标是「让输出更易读」。

(三)运算符重载:让自定义类支持数学运算与比较

用于给自定义类赋予运算符行为(如 +-==< 等),避免编写繁琐的普通方法(如 add()compare())。

魔法方法 触发场景 核心作用 示例代码(自定义向量类)
__add__(self, other) self + other(加法) 实现两个实例的加法运算 python class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): # 向量加法:(x1+x2, y1+y2) return Vector(self.x + other.x, self.y + other.y) def __str__(self): return f"Vector({self.x}, {self.y})" # 使用:v1 = Vector(1,2); v2 = Vector(3,4); print(v1+v2) → Vector(4, 6)
__sub__(self, other) self - other(减法) 实现两个实例的减法运算 python def __sub__(self, other): return Vector(self.x - other.x, self.y - other.y)
__mul__(self, other) self * other(乘法) 实现实例与标量/实例的乘法运算 python def __mul__(self, scalar): # 向量标量乘法:(x*s, y*s) return Vector(self.x * scalar, self.y * scalar)
__truediv__(self, other) self / other(除法) 实现实例与标量/实例的除法运算 python def __truediv__(self, scalar): return Vector(self.x / scalar, self.y / scalar)
__eq__(self, other) self == other(等于) 定义实例相等的判断逻辑 python def __eq__(self, other): return self.x == other.x and self.y == other.y # 使用:v1 == v2 → False
__lt__(self, other) self < other(小于) 定义实例小于的判断逻辑 python def __lt__(self, other): # 比较向量模长:sqrt(x²+y²) return (self.x**2 + self.y**2) < (other.x**2 + other.y**2) # 使用:v1 < v2 → True
__ge__(self, other) self >= other(大于等于) 定义实例大于等于的判断逻辑 python def __ge__(self, other): return (self.x**2 + self.y**2) >= (other.x**2 + other.y**2)

关键说明:

  • 运算符重载需遵循「语义一致性」(如 __add__ 仅实现加法,不滥用为其他逻辑);
  • 比较运算符(==< 等)若未实现,默认比较实例的内存地址(id)。

(四)容器行为:让自定义类支持列表/字典式操作

用于给自定义类赋予容器特性(如索引、切片、长度、迭代),适合封装「集合型数据」(如自定义列表、缓存池、数据集)。

魔法方法 触发场景 核心作用 示例代码(自定义列表类)
__len__(self) len(self) 时触发 返回实例的长度(元素个数) python class MyList: def __init__(self, data): self.data = list(data) def __len__(self): return len(self.data) # 使用:ml = MyList([1,2,3]); len(ml) → 3
__getitem__(self, key) self[key](索引/切片)时触发 实现实例的索引访问、切片功能 python def __getitem__(self, key): return self.data[key] # 使用:ml[0] → 1;ml[1:3] → [2,3]
__setitem__(self, key, value) self[key] = value 时触发 实现实例的索引赋值功能 python def __setitem__(self, key, value): self.data[key] = value # 使用:ml[0] = 100 → ml.data 变成 [100,2,3]
__delitem__(self, key) del self[key] 时触发 实现实例的索引删除功能 python def __delitem__(self, key): del self.data[key] # 使用:del ml[0] → ml.data 变成 [2,3]
__iter__(self) for x in self 时触发 实现实例的迭代功能(支持 for 循环) python def __iter__(self): return iter(self.data) # 使用:for x in ml: print(x) → 1、2、3
__contains__(self, x) x in self 时触发 实现实例的包含判断功能 python def __contains__(self, x): return x in self.data # 使用:2 in ml → True

示例使用完整流程:

ml = MyList([1, 2, 3, 4])
print(len(ml))       # 触发 __len__ → 4
print(ml[1:3])       # 触发 __getitem__ → [2, 3]
ml[0] = 100          # 触发 __setitem__ → ml.data = [100, 2, 3, 4]
print(2 in ml)       # 触发 __contains__ → True
for x in ml:         # 触发 __iter__ → 遍历输出 100、2、3、4
    print(x)
del ml[1]            # 触发 __delitem__ → ml.data = [100, 3, 4]