Python 中常用的魔法方法

魔法方法(Magic Methods),也称为特殊方法(Special Methods)或双下方法(Dunder Methods),是 Python 中由双下划线(__)包围的特殊方法。它们允许类定义自己的行为,使其能够与 Python 的内置操作符和函数一起工作。

一、基本魔法方法

1. 对象生命周期相关

  • __new__(cls, [...]):实例创建时第一个调用的方法

  • __init__(self, [...]):构造器,初始化实例

  • __del__(self):析构器,对象销毁时调用

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("Creating instance")
        instance = super().__new__(cls)
        return instance
        
    def __init__(self, value):
        print("Initializing instance")
        self.value = value
        
    def __del__(self):
        print("Instance is being destroyed")

obj = MyClass(10)  # 输出: Creating instance 和 Initializing instance
del obj            # 输出: Instance is being destroyed

2. 字符串表示

  • __str__(self)str(obj) 和 print(obj) 时调用

  • __repr__(self)repr(obj) 和交互式解释器中直接显示对象时调用

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = 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)
print(repr(p)) # 输出: Point(x=1, y=2)

二、容器类型魔法方法

1. 序列和映射方法

  • __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 时调用

class MyList:
    def __init__(self, data):
        self.data = list(data)
        
    def __len__(self):
        return len(self.data)
        
    def __getitem__(self, index):
        return self.data[index]
        
    def __setitem__(self, index, value):
        self.data[index] = value
        
    def __delitem__(self, index):
        del self.data[index]
        
    def __contains__(self, item):
        return item in self.data

ml = MyList([1, 2, 3])
print(len(ml))    # 3
print(ml[1])      # 2
print(3 in ml)    # True

2. 迭代器协议

  • __iter__(self)iter(obj) 或 for x in obj 时调用

  • __next__(self):迭代时获取下一个值

class CountDown:
    def __init__(self, start):
        self.current = start
        
    def __iter__(self):
        return self
        
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        else:
            self.current -= 1
            return self.current + 1

for num in CountDown(3):
    print(num)  # 输出: 3, 2, 1

三、数值运算魔法方法

1. 基本算术运算

  • __add__(self, other)+ 运算

  • __sub__(self, other)- 运算

  • __mul__(self, other)* 运算

  • __truediv__(self, other)/ 运算

  • __floordiv__(self, other)// 运算

  • __mod__(self, other)% 运算

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
        
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)
        
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
        
    def __str__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(2, 4)
v2 = Vector(1, 3)
print(v1 + v2)  # Vector(3, 7)
print(v1 * 3)   # Vector(6, 12)

2. 比较运算

  • __eq__(self, other)== 运算

  • __ne__(self, other)!= 运算

  • __lt__(self, other)< 运算

  • __le__(self, other)<= 运算

  • __gt__(self, other)> 运算

  • __ge__(self, other)>= 运算

class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius
        
    def __eq__(self, other):
        return self.celsius == other.celsius
        
    def __lt__(self, other):
        return self.celsius < other.celsius
        
    def __str__(self):
        return f"{self.celsius}°C"

t1 = Temperature(20)
t2 = Temperature(30)
print(t1 < t2)  # True
print(t1 == t2) # False

四、上下文管理

  • __enter__(self):进入 with 代码块时调用

  • __exit__(self, exc_type, exc_val, exc_tb):退出 with 代码块时调用

class Timer:
    def __enter__(self):
        import time
        self.start = time.time()
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        self.end = time.time()
        print(f"Elapsed time: {self.end - self.start:.2f} seconds")

with Timer():
    # 执行一些代码
    sum(range(1000000))

五、属性访问控制

  • __getattr__(self, name):访问不存在的属性时调用

  • __setattr__(self, name, value):设置属性时调用

  • __delattr__(self, name):删除属性时调用

  • __getattribute__(self, name):访问任何属性时调用(慎用)

class Restricted:
    def __init__(self):
        self.allowed = "This is allowed"
        
    def __getattr__(self, name):
        return f"Access to '{name}' is not allowed"
        
    def __setattr__(self, name, value):
        if name == 'allowed':
            super().__setattr__(name, value)
        else:
            print(f"Cannot set attribute '{name}'")

r = Restricted()
print(r.allowed)    # This is allowed
print(r.forbidden)  # Access to 'forbidden' is not allowed
r.forbidden = 123   # Cannot set attribute 'forbidden'

六、调用模拟

  • __call__(self, [...]):使实例可以像函数一样被调用

class Adder:
    def __init__(self, n):
        self.n = n
        
    def __call__(self, x):
        return self.n + x

add5 = Adder(5)
print(add5(3))  # 8

七、其他实用魔法方法

  • __slots__:限制实例属性,节省内存

  • __hash__(self)hash(obj) 时调用

  • __bool__(self)bool(obj) 时调用

  • __dir__(self)dir(obj) 时调用

class Limited:
    __slots__ = ['a', 'b']  # 只能有a和b两个属性
    
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
    def __bool__(self):
        return bool(self.a or self.b)

l = Limited(0, 1)
print(bool(l))  # True

最佳实践

  1. 仅在需要时实现魔法方法:不要过度使用,只在需要特殊行为时实现

  2. 保持一致性:实现比较操作时,应实现全套比较方法

  3. 遵循约定:如 __eq__ 应该与 __hash__ 一起实现

  4. 文档化:为魔法方法添加文档字符串,说明其行为

魔法方法是 Python 强大灵活性的重要组成部分,合理使用可以使你的类更加 Pythonic 和易用。

posted @ 2025-06-19 18:31  郭慕荣  阅读(149)  评论(0)    收藏  举报