当前位置:首页>python>Python 中魔法函数(Magic Methods)全面系统详解

Python 中魔法函数(Magic Methods)全面系统详解

  • 2026-10-04 17:07:32
Python 中魔法函数(Magic Methods)全面系统详解

Python 中的魔法函数(Magic Methods),又称特殊方法(Special Methods)或双下方法(Dunder Methods,其名称以双下划线__ 开头和结尾),是一组预定义的方法名,用于让自定义类能够模拟 Python 内置类型的行为,并支持语言的核心语法(如运算符、属性访问、函数调用等)。通过实现这些方法,可以使自定义对象表现得像内置对象一样自然,从而充分利用 Python 的灵活性和一致性。

本文将全面、详细地介绍 Python 中常用的魔法函数,按功能分类讲解每个方法的用途、参数、返回值、调用时机以及实际代码示例。


1. 对象生命周期与初始化

方法
描述
__new__(cls[, ...])
创建并返回一个新实例。在 __init__ 之前调用,是真正的构造函数(通常用于不可变类型或控制实例创建)。
__init__(self[, ...])
初始化已创建的实例。在 __new__ 之后调用,负责设置对象的初始状态。
__del__(self)
析构函数,当对象被垃圾回收时调用(注意:不保证立即调用,除非明确 del 且引用计数归零)。

示例:

classSingleton:    _instance = Nonedef__new__(cls, *args, **kwargs):ifnot cls._instance:            cls._instance = super().__new__(cls)return cls._instancedef__init__(self, value):        self.value = valuea = Singleton(10)b = Singleton(20)print(a is b)  # Trueprint(a.value, b.value)                                      
20 20(因为共享同一个实例,__init__ 被每次调用覆盖)

2. 对象字符串表示

方法
描述
__repr__(self)
返回对象的“官方”字符串表示,通常应能被 eval() 重建该对象(或给出清晰信息)。由 repr() 调用,也是 print() 回退时使用。
__str__(self)
返回对象的“非正式”字符串表示,对用户友好。由 str() 和 print() 调用。未定义时回退到 __repr__。
__format__(self, format_spec)
由 format() 和 f-string 调用,实现自定义格式化。
__bytes__(self)
返回对象的字节串表示,由 bytes() 调用。

示例:

classPoint:def__init__(self, x, y):        self.x = x        self.y = ydef__repr__(self):return f"Point({self.x}, {self.y})"def__str__(self):return f"({self.x}, {self.y})"def__format__(self, spec):if spec == 'polar':import math            r = math.hypot(self.x, self.y)            theta = math.atan2(self.y, self.x)returnf"(r={r:.2f}, θ={theta:.2f})"return str(self)p = Point(3, 4)print(repr(p))     print(str(p))       print(f"{p:polar}") 
(r=5.00, θ=0.93)
(3, 4)
Point(3, 4)

3. 比较与哈希

方法
描述
__lt__(self, other)
小于 <
__le__(self, other)
小于等于 <=
__eq__(self, other)
等于 ==
__ne__(self, other)
不等于 !=
__gt__(self, other)
大于 >
__ge__(self, other)
大于等于 >=
__hash__(self)
由 hash() 调用,返回整数哈希值。如果对象是可哈希的(如用作字典键),需要实现此方法,通常与 __eq__ 一致。

注意: 如果定义了 __eq__ 而没有定义 __hash__,则实例会变成不可哈希的。如果希望实例可变,通常不定义 __hash__ 或设为 None。

示例:

classPerson:def__init__(self, name, age):        self.name = name        self.age = agedef__eq__(self, other):ifnot isinstance(other, Person):returnNotImplementedreturn self.name == other.name and self.age == other.agedef__hash__(self):return hash((self.name, self.age))def__lt__(self, other):ifnot isinstance(other, Person):returnNotImplementedreturn self.age < other.agep1 = Person("Alice", 30)p2 = Person("Alice", 30)p3 = Person("Bob", 25)print(p1 == p2)        # Trueprint(p1 < p3)         # Falseprint(hash(p1) == hash(p2))  # True

4. 属性访问控制

方法
描述
__getattr__(self, name)
当访问的属性不存在时被调用(属性查找失败时)。
__getattribute__(self, name)
所有属性访问都会先经过此方法(除非有显式优化)。需谨慎使用,避免无限递归。
__setattr__(self, name, value)
设置属性时调用。
__delattr__(self, name)
删除属性时调用。
__dir__(self)
由 dir() 调用,返回属性列表。

示例:

classDynamicProxy:def__init__(self, obj):        self._obj = objdef__getattr__(self, name):        print(f"Delegating {name}")return getattr(self._obj, name)def__setattr__(self, name, value):if name == "_obj":            super().__setattr__(name, value)else:            setattr(self._obj, name, value)classData:    x = 10d = Data()proxy = DynamicProxy(d)print(proxy.x)   # 输出 Delegating x 然后 10proxy.y = 20print(d.y)       # 20

5. 容器与序列模拟

方法
描述
__len__(self)
返回容器长度,由 len() 调用。
__getitem__(self, key)
支持 self[key] 索引访问。
__setitem__(self, key, value)
支持 self[key] = value 赋值。
__delitem__(self, key)
支持 del self[key] 删除。
__contains__(self, item)
支持 item in self。未定义时使用 __iter__ 遍历查找。
__iter__(self)
返回一个迭代器,由 iter() 调用。
__next__(self)
迭代器的下一个元素,由 next() 调用(迭代器类需实现)。
__reversed__(self)
由 reversed() 调用,返回反向迭代器。
__missing__(self, key)
当在字典子类中 __getitem__ 找不到键时调用(仅用于 dict 子类)。

示例(不可变序列):

classRange:def__init__(self, start, stop, step=1):        self.start = start        self.stop = stop        self.step = stepdef__len__(self):return max(0, (self.stop - self.start + self.step - 1) // self.step)def__getitem__(self, index):if index < 0:            index += len(self)if0 <= index < len(self):return self.start + index * self.stepraise IndexError("index out of range")r = Range(1, 10, 2)print(len(r))        # 5print(r[2])          # 5print(5in r)        # True(因为定义了 __getitem__ 和 __len__,Python 可以自动处理迭代和成员测试)for v in r:    print(v)         # 1 3 5 7 9

6. 数值运算魔法函数

Python 允许通过魔法函数重载所有运算符。以下分类列举:

6.1 一元运算符

方法
描述
__neg__(self)
负号 -obj
__pos__(self)
正号 +obj
__abs__(self)
绝对值 abs(obj)
__invert__(self)
按位取反 ~obj
__round__(self, n)round(obj, n)
__floor__(self)math.floor(obj)
__ceil__(self)math.ceil(obj)
__trunc__(self)math.trunc(obj)

6.2 二元算术运算符

方法
描述
__add__(self, other)
加法 +
__sub__(self, other)
减法 -
__mul__(self, other)
乘法 *
__matmul__(self, other)
矩阵乘法 @(Python 3.5+)
__truediv__(self, other)
真除法 /
__floordiv__(self, other)
整除 //
__mod__(self, other)
取模 %
__divmod__(self, other)divmod(obj, other)
__pow__(self, other[, modulo])
幂 ** 或 pow()
__lshift__(self, other)
左移 <<
__rshift__(self, other)
右移 >>
__and__(self, other)
按位与 &
__xor__(self, other)
按位异或 ^
__or__(self, other)
按位或 |

6.3 反射(反向)算术运算符

当左操作数不支持相应操作时,Python 会尝试右操作数的反射方法,形式为 __r*__(如 __radd__)。这些方法参数相同,用于实现交换律。

6.4 增量赋值运算符

形式为 __i*__(如 __iadd__),用于 +=、-= 等。如果未定义,Python 会退化为普通运算符赋值(a = a + b),但效率较低且可能改变对象身份。

6.5 类型转换

方法
描述
__int__(self)
转换为整数,由 int() 调用
__float__(self)
转换为浮点数,由 float() 调用
__complex__(self)
转换为复数,由 complex() 调用
__bool__(self)
转换为布尔值,由 bool() 调用(未定义时返回 len(self) != 0 或 True)
__index__(self)
转换为整数用于切片索引等(必须返回整数),由 operator.index() 调用

完整示例(复数向量类):

import mathclassVector:def__init__(self, x, y):        self.x = x        self.y = ydef__repr__(self):return f"Vector({self.x}, {self.y})"# 加法def__add__(self, other):if isinstance(other, Vector):return Vector(self.x + other.x, self.y + other.y)returnNotImplemented# 反射加法(确保 vector + number 可以交换)def__radd__(self, other):return self.__add__(other)   # 如果 other 是标量,可自行处理# 绝对值:模长def__abs__(self):return math.hypot(self.x, self.y)# 布尔值:模长非零def__bool__(self):return bool(abs(self))# 支持向量乘以标量def__mul__(self, scalar):if isinstance(scalar, (int, float)):return Vector(self.x * scalar, self.y * scalar)return NotImplemented    __rmul__ = __mul__   # 交换律v1 = Vector(3, 4)v2 = Vector(1, 2)print(v1 + v2)      # Vector(4, 6)print(2 * v1)       # Vector(6, 8)print(abs(v1))      # 5.0print(bool(v1))     # True

7. 可调用对象

方法
描述
__call__(self[, ...])
使实例可以像函数一样被调用:obj()。

示例:

classAdder:def__init__(self, n):        self.n = ndef__call__(self, x):return self.n + xadd_five = Adder(5)print(add_five(3))   # 8print(callable(add_five))  # True

8. 上下文管理器

方法
描述
__enter__(self)
进入 with 块时调用,返回值绑定到 as 后的变量。
__exit__(self, exc_type, exc_value, traceback)
退出 with 块时调用,负责清理资源,返回 True 表示抑制异常。

示例:

classManagedFile:def__init__(self, filename, mode):        self.filename = filename        self.mode = modedef__enter__(self):        self.file = open(self.filename, self.mode)return self.filedef__exit__(self, exc_type, exc_val, exc_tb):if self.file:            self.file.close()if exc_type:            print(f"Exception occurred: {exc_val}")return True# 抑制异常(通常不这样做,除非特殊需求)with ManagedFile('test.txt', 'w') as f:    f.write('hello')# 文件自动关闭,即使写操作异常也会处理

9. 描述符协议

描述符是实现了特定方法的类,用于管理另一个类的属性访问。常用在 @property、staticmethod、classmethod 等机制中。

方法
描述
__get__(self, instance, owner)
获取属性值。instance 是拥有该属性的实例,owner 是拥有者类。
__set__(self, instance, value)
设置属性值。
__delete__(self, instance)
删除属性。
__set_name__(self, owner, name)
Python 3.6+,在类创建时被调用,告知描述符在类中的属性名。

示例:

classPositiveNumber:def__set_name__(self, owner, name):        self.name = namedef__get__(self, instance, owner):if instance isNone:return selfreturn instance.__dict__.get(self.name, 0)def__set__(self, instance, value):if value < 0:raise ValueError("Value must be positive")        instance.__dict__[self.name] = valueclassOrder:    quantity = PositiveNumber()    price = PositiveNumber()def__init__(self, quantity, price):        self.quantity = quantity        self.price = pricedeftotal(self):return self.quantity * self.priceorder = Order(10, 5)print(order.total())  # 50# order.quantity = -10  # ValueError

10. 其他常用魔法函数

方法
描述
__instancecheck__(self, instance)
自定义 isinstance() 行为(用于元类)。
__subclasscheck__(self, subclass)
自定义 issubclass() 行为(用于元类)。
__class__
属性,通常返回对象的类,也可通过元类自定义。
__slots__
类变量,限制实例可拥有的属性,节省内存。
__init_subclass__(cls, **kwargs)
Python 3.6+,当子类被创建时调用,用于注册或修改子类。
__set_name__
已在描述符中介绍。
__dir__
已在属性访问中介绍。
__sizeof__(self)
返回对象的内存大小(字节),由 sys.getsizeof() 调用。
__copy__(self)
 和 __deepcopy__(self, memo)
用于支持 copy 模块的自定义复制。
__reduce__(self)
 和 __reduce_ex__(self, protocol)
用于 pickle 序列化。

示例(__slots__):

classPoint:    __slots__ = ('x', 'y')def__init__(self, x, y):        self.x = x        self.y = yp = Point(1, 2)# p.z = 3  # AttributeError: 'Point' object has no attribute 'z'print(p.__slots__)  # ('x', 'y')

示例(__init_subclass__):

classPluginBase:    plugins = []def__init_subclass__(cls, **kwargs):        super().__init_subclass__(**kwargs)        cls.plugins.append(cls)classPluginA(PluginBase):passclassPluginB(PluginBase):passprint(PluginBase.plugins)  # [<class '__main__.PluginA'>, <class '__main__.PluginB'>]

总结

Python 的魔法函数是语言数据模型的核心,它们允许编写的类无缝集成到 Python 的语法和内置操作中。掌握这些方法不仅可以写出更符合 Python 风格的代码,还能深入理解 Python 内部的工作机制。

使用魔法函数时,应遵循以下原则:

  • 尽量模拟内置类型的行为,保持一致性。
  • 在操作不支持的类型时,返回 NotImplemented 而不是抛出异常,以便 Python 尝试反向操作。
  • 避免过度使用,保持代码简洁可读。
  • 熟悉文档(Python 数据模型)以了解所有细节。

最新文章

随机文章