🧙♂️ 为什么叫"魔术"?

因为当你写 a + b、len(obj)、str(obj) 等代码时,Python 会自动调用对应的魔术方法,就像变魔术一样!


✅ 常见魔术方法分类与示例


🔹 1. 对象创建与初始化

表格

方法触发时机说明
__new__(cls, ...)创建对象实例时控制对象创建(很少重写)
__init__(self, ...)对象初始化时初始化属性(最常用)
__del__(self)对象被销毁时析构函数(慎用)
class Person:
    def __init__(self, name):
        self.name = name

🔹 2. 字符串表示

表格

方法触发时机用途
__str__(self)str(obj), print(obj)面向用户的可读字符串
__repr__(self)repr(obj), 调试/交互式环境面向开发者的精确表示(理想情况下应能 eval(repr(obj)) == obj)
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __str__(self):
        return f"Point({self.x}, {self.y})"
    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

p = Point(1, 2)
print(p)        # Point(1, 2)
repr(p)         # 'Point(x=1, y=2)'

🔹 3. 算术运算符

表格

方法表达式说明
__add__(self, other)a + b加法
__sub__(self, other)a - b减法
__mul__(self, other)a * b乘法
__truediv__(self, other)a / b真除法
__floordiv__(self, other)a // b整除
__mod__(self, other)a % b取模
__pow__(self, other)a ** b幂运算

💡 还有反向方法(如 __radd__)用于 1 + obj(当左边不是你的类时)

class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2  # 自动调用 __add__

🔹 4. 比较运算符

表格

方法表达式
__eq__(self, other)a == b
__ne__(self, other)a != b
__lt__(self, other)a < b
__le__(self, other)a <= b
__gt__(self, other)a > b
__ge__(self, other)a >= b

✅ 只需实现 __eq__ 和 __lt__,再用 @functools.total_ordering 自动生成其他比较方法。


🔹 5. 容器/序列行为

表格

方法触发时机
__len__(self)len(obj)
__getitem__(self, key)obj[key]
__setitem__(self, key, value)obj[key] = value
__delitem__(self, key)del obj[key]
__contains__(self, item)item in obj
__iter__(self)for x in obj(返回迭代器)
__next__(self)迭代器协议(配合 __iter__)
class MyList:
    def __init__(self, data):
        self.data = data
    def __len__(self):
        return len(self.data)
    def __getitem__(self, index):
        return self.data[index]

mylist = MyList([1, 2, 3])
print(len(mylist))      # 3
print(mylist[0])        # 1

🔹 6. 调用对象像函数

表格

方法触发时机
__call__(self, ...)obj()
class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    def __call__(self, x):
        return x * self.factor

double = Multiplier(2)
print(double(5))  # 10

🔹 7. 上下文管理器(with 语句)

表格

方法触发时机
__enter__(self)进入 with 块
__exit__(self, exc_type, exc_val, exc_tb)退出 with 块
class FileManager:
    def __init__(self, filename):
        self.filename = filename
    def __enter__(self):
        self.file = open(self.filename, 'w')
        return self.file
    def __exit__(self, *args):
        self.file.close()

with FileManager('test.txt') as f:
    f.write('hello')
# 自动关闭文件

🔹 8. 属性访问控制

表格

方法触发时机
__getattr__(self, name)访问不存在的属性时
__getattribute__(self, name)所有属性访问(慎用!)
__setattr__(self, name, value)设置属性时
__delattr__(self, name)删除属性时
class SafeDict:
    def __init__(self):
        self._data = {}
    def __getattr__(self, name):
        return self._data.get(name, "Not found")

d = SafeDict()
print(d.x)  # "Not found"

🔹 9. 布尔值与真值测试

表格

方法触发时机
__bool__(self)bool(obj), if obj:
__len__(self)如果没定义 __bool__,则用 len(obj) != 0 判断
class Truthy:
    def __init__(self, value):
        self.value = value
    def __bool__(self):
        return self.value > 0

t = Truthy(5)
if t: print("Positive!")  # 打印

✅ 总结:常用魔术方法速查表

表格

功能推荐实现的方法
打印友好__str__, __repr__
数学运算__add__, __mul__, …
比较大小__eq__, __lt__ (+ @total_ordering)
容器行为__len__, __getitem__, __iter__
函数调用__call__
with 语句__enter__, __exit__
属性拦截__getattr__, __setattr__

⚠️ 注意事项:

  1. 不要滥用:只在需要自定义行为时才重写。
  2. 保持一致性:比如实现了 __eq__,通常也要实现 __hash__(如果对象可哈希)。
  3. 使用 functools.total_ordering 简化比较逻辑。
  4. 用 @property 代替 __get__/__set__ 大多数场景更简单。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐