在 Python 中,类的特殊方法(也称为"魔术方法"或"双下方法")是实现运算符重载和定制类行为的关键。这些方法以双下划线开头和结尾(如 __init__),让开发者能够定义对象如何响应内置操作。
1. 什么是特殊方法?
特殊方法是 Python 中预定义的方法,用于实现类的特定行为:
2. 核心价值
- 自然语法:让自定义类支持 obj1 + obj2等自然操作
- 代码简洁:减少样板代码,提高可读性
- 高度集成:使自定义类无缝融入 Python 生态系统
1. 对象创建与初始化
|  |  |  | 
|---|
| __new__ |  | obj = MyClass() | 
| __init__ |  | obj = MyClass() | 
| __del__ |  |  | 
class Resource:
    def __init__(self, name):
        self.name = name
        print(f"资源 {name} 初始化")
    
    def __del__(self):
        print(f"资源 {self.name} 释放")
# 使用
res = Resource("数据库连接")
del res  # 输出: 资源 数据库连接 释放
2. 字符串表示
|  |  |  | 
|---|
| __str__ |  | print(obj) | 
| __repr__ |  | repr(obj) | 
| __format__ |  | format(obj, spec) | 
class Point:
    def__init__(self, x, y):
        self.x = x
        self.y = y
    
    def__str__(self):
        returnf"点({self.x}, {self.y})"
    
    def__repr__(self):
        returnf"Point(x={self.x}, y={self.y})"
    
    def__format__(self, format_spec):
        if format_spec == 'polar':
            r = (self.x**2 + self.y**2)**0.5
            theta = math.degrees(math.atan2(self.y, self.x))
            returnf"极坐标: ({r:.2f}, {theta:.1f}°)"
        returnstr(self)
# 使用
p = Point(3, 4)
print(p)        # 点(3, 4)
print(repr(p))  # Point(x=3, y=4)
print(f"{p}")   # 点(3, 4)
print(f"{p:polar}")  # 极坐标: (5.00, 53.1°)
3. 运算符重载
算术运算符
|  |  |  | 
|---|
| __add__ | + |  | 
| __sub__ | - |  | 
| __mul__ | * |  | 
| __truediv__ | / |  | 
| __floordiv__ | // |  | 
| __mod__ | % |  | 
| __pow__ | ** |  | 
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__mul__(self, scalar):
        ifisinstance(scalar, (int, float)):
            return Vector(self.x * scalar, self.y * scalar)
        raise TypeError("标量必须是数值")
    
    def__str__(self):
        returnf"({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(1, 4)
print(v1 + v2)  # (3, 7)
print(v1 * 3)   # (6, 9)
比较运算符
|  |  |  | 
|---|
| __eq__ | == |  | 
| __ne__ | != |  | 
| __lt__ | < |  | 
| __le__ | <= |  | 
| __gt__ | > |  | 
| __ge__ | >= |  | 
class Temperature:
    def__init__(self, celsius):
        self.celsius = celsius
    
    def__eq__(self, other):
        returnself.celsius == other.celsius
    
    def__lt__(self, other):
        returnself.celsius < other.celsius
    
    def__str__(self):
        returnf"{self.celsius}°C"
t1 = Temperature(25)
t2 = Temperature(30)
print(t1 == t2)  # False
print(t1 < t2)   # True
反运算方法
当左操作数不支持运算时,Python 会尝试右操作数的反运算方法:
- __radd__:- other + self
- __rsub__:- other - self
- __rmul__:- other * self
class Vector:
    # ... 其他方法同上
    
    def __rmul__(self, scalar):
        return self.__mul__(scalar)
v = Vector(2, 3)
print(3 * v)  # 调用 __rmul__: (6, 9)
4. 容器类型方法
|  |  |  | 
|---|
| __len__ |  | len(obj) | 
| __getitem__ |  | obj[key] | 
| __setitem__ |  | obj[key] = value | 
| __delitem__ |  | del obj[key] | 
| __contains__ |  | item in obj | 
class Matrix:
    def__init__(self, rows, cols):
        self.data = [[0] * cols for _ inrange(rows)]
        self.rows = rows
        self.cols = cols
    
    def__len__(self):
        returnself.rows
    
    def__getitem__(self, index):
        ifisinstance(index, int):
            returnself.data[index]
        elifisinstance(index, tuple) andlen(index) == 2:
            row, col = index
            returnself.data[row][col]
        raise TypeError("无效索引")
    
    def__setitem__(self, index, value):
        ifisinstance(index, tuple) andlen(index) == 2:
            row, col = index
            self.data[row][col] = value
        else:
            raise TypeError("需要(row, col)索引")
    
    def__contains__(self, value):
        returnany(value in row for row inself.data)
# 使用
m = Matrix(3, 3)
m[1, 1] = 5# 设置中心元素
print(m[1, 1])  # 5
print(5in m)   # True
5. 可调用对象
class Counter:
    def__init__(self):
        self.count = 0
    
    def__call__(self):
        self.count += 1
        returnself.count
# 使用
counter = Counter()
print(counter())  # 1
print(counter())  # 2
6. 上下文管理
|  |  |  | 
|---|
| __enter__ |  | with obj: | 
| __exit__ |  |  | 
class Timer:
    def__enter__(self):
        self.start = time.time()
        returnself
    
    def__exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.time()
        print(f"耗时: {self.end - self.start:.4f}秒")
    
    defelapsed(self):
        return time.time() - self.start
# 使用
with Timer() as t:
    time.sleep(1.5)
    print(f"已用时间: {t.elapsed():.2f}秒")
# 输出:
# 已用时间: 1.50秒
# 耗时: 1.5012秒
1. 属性访问控制
|  |  |  | 
|---|
| __getattr__ |  | obj.undefined | 
| __getattribute__ |  | obj.any_attr | 
| __setattr__ |  | obj.attr = value | 
| __delattr__ |  | del obj.attr | 
class ImmutablePoint:
    def__init__(self, x, y):
        super().__setattr__('x', x)
        super().__setattr__('y', y)
    
    def__setattr__(self, name, value):
        raise AttributeError("ImmutablePoint 对象不可修改")
    
    def__delattr__(self, name):
        raise AttributeError("ImmutablePoint 对象不可删除属性")
# 使用
p = ImmutablePoint(3, 4)
print(p.x)  # 3
p.x = 5    # AttributeError
2. 数值类型扩展
|  |  | 
|---|
| __abs__ | abs(obj) | 
| __neg__ | -obj | 
| __pos__ | +obj | 
| __invert__ | ~obj | 
class ComplexNumber:
    def__init__(self, real, imag):
        self.real = real
        self.imag = imag
    
    def__abs__(self):
        return (self.real**2 + self.imag**2)**0.5
    
    def__neg__(self):
        return ComplexNumber(-self.real, -self.imag)
    
    def__str__(self):
        returnf"{self.real}{self.imag:+}i"
# 使用
c = ComplexNumber(3, 4)
print(abs(c))  # 5.0
print(-c)     # -3-4i
3. 反射方法
用于支持内置函数 isinstance() 和 issubclass():
|  |  | 
|---|
| __instancecheck__ | isinstance(obj, cls) | 
| __subclasscheck__ | issubclass(sub, cls) | 
class Meta(type):
    def__instancecheck__(cls, instance):
        print(f"检查实例: {instance}")
        returnhasattr(instance, 'special_attr')
    
    def__subclasscheck__(cls, subclass):
        print(f"检查子类: {subclass}")
        returnhasattr(subclass, 'required_method')
classBase(metaclass=Meta):
    pass
classChild(Base):
    required_method = lambda: None
obj = type('X', (), {'special_attr': True})()
print(isinstance(obj, Base))  # True
print(issubclass(Child, Base))  # True
1. 设计原则
- 一致性:保持特殊方法行为与内置类型一致
- 最小惊讶原则:避免反直觉的实现
- 性能考量:复杂计算应考虑缓存或延迟计算
- 文档完备:为特殊方法添加详细文档字符串
2. 常见错误
# 错误1:无限递归
classBadExample:
    def__init__(self, value):
        self.value = value  # 调用 __setattr__
    
    def__setattr__(self, name, value):
        self.name = value  # 无限递归!
# 正确做法
classGoodExample:
    def__init__(self, value):
        super().__setattr__('value', value)
    
    def__setattr__(self, name, value):
        super().__setattr__(name, value)
# 错误2:忽略不可变类型
classVector:
    def__init__(self, x, y):
        self.x = x
        self.y = y
    
    def__add__(self, other):
        # 错误:原地修改
        self.x += other.x
        self.y += other.y
        returnself
    
# 正确做法(返回新对象)
classCorrectVector:
    def__init__(self, x, y):
        self.x = x
        self.y = y
    
    def__add__(self, other):
        return CorrectVector(self.x + other.x, self.y + other.y)
3. 优化技巧
- 避免不必要的计算: - class LazyProperty:
 def__init__(self, func):
 self.func = func
 self.name = func.__name__
 
 def__get__(self, obj, cls):
 if obj isNone:
 returnself
 value = self.func(obj)
 setattr(obj, self.name, value)  # 缓存结果
 return value
 
 classCircle:
 def__init__(self, radius):
 self.radius = radius
 
 @LazyProperty
 defarea(self):
 print("计算面积...")
 return3.14 * self.radius ** 2
 
 c = Circle(5)
 print(c.area)  # 第一次计算
 print(c.area)  # 使用缓存
 
 
- 减少临时对象: - class Vector:
 # ... 其他方法
 
 def __iadd__(self, other):
 """实现 += 原地操作"""
 self.x += other.x
 self.y += other.y
 return self
 
 
4. 使用场景推荐
|  |  |  | 
|---|
|  | __add__ |  | 
|  | __getitem__ |  | 
|  | __enter__ |  | 
|  | __call__ |  | 
|  | __getattribute__ |  | 
|  | __new__ |  | 
1. 实现自定义范围类型
class InclusiveRange:
    """支持步长的包含范围 [start, end]"""
    
    def__init__(self, start, end, step=1):
        if step == 0:
            raise ValueError("步长不能为零")
        self.start = start
        self.end = end
        self.step = step
    
    def__iter__(self):
        current = self.start
        ifself.step > 0:
            while current <= self.end:
                yield current
                current += self.step
        else:
            while current >= self.end:
                yield current
                current += self.step
    
    def__len__(self):
        ifself.step > 0:
            returnmax(0, (self.end - self.start) // self.step + 1)
        else:
            returnmax(0, (self.start - self.end) // (-self.step) + 1)
    
    def__contains__(self, item):
        ifnotisinstance(item, (int, float)):
            returnFalse
        
        ifself.step > 0:
            if item < self.start or item > self.end:
                returnFalse
        else:
            if item > self.start or item < self.end:
                returnFalse
        
        return (item - self.start) % self.step == 0
    
    def__reversed__(self):
        return InclusiveRange(self.end, self.start, -self.step)
    
    def__str__(self):
        returnf"[{self.start}..{self.end}]" + (
            f" step {self.step}"ifself.step != 1else""
        )
# 使用
r = InclusiveRange(1, 10, 2)
print(list(r))       # [1, 3, 5, 7, 9]
print(len(r))        # 5
print(5in r)       # True
print(list(reversed(r)))  # [9, 7, 5, 3, 1]
2. 实现科学计算向量
class ScientificVector:
    """支持基本运算和科学函数的向量"""
    
    def__init__(self, *components):
        self.components = components
        self.dim = len(components)
    
    def__add__(self, other):
        self._check_dim(other)
        return ScientificVector(*(
            x + y for x, y inzip(self.components, other.components)
        )
    
    def__sub__(self, other):
        self._check_dim(other)
        return ScientificVector(*(
            x - y for x, y inzip(self.components, other.components)
        )
    
    def__mul__(self, scalar):
        ifnotisinstance(scalar, (int, float)):
            raise TypeError("标量必须是数值")
        return ScientificVector(*(
            x * scalar for x inself.components
        ))
    
    def__matmul__(self, other):
        """点积运算 @"""
        self._check_dim(other)
        returnsum(x * y for x, y inzip(self.components, other.components))
    
    def__abs__(self):
        """向量模长"""
        returnsum(x**2for x inself.components)**0.5
    
    def__round__(self, n=None):
        """四舍五入"""
        return ScientificVector(*(
            round(x, n) for x inself.components
        ))
    
    def_check_dim(self, other):
        ifself.dim != other.dim:
            raise ValueError("向量维度不匹配")
    
    def__str__(self):
        returnf"Vector{self.components}"
    
    def__repr__(self):
        returnf"ScientificVector{self.components}"
# 使用
v1 = ScientificVector(1.2, 2.3, 3.4)
v2 = ScientificVector(0.8, 1.7, 2.6)
print(v1 + v2)        # Vector(2.0, 4.0, 6.0)
print(v1 @ v2)        # 1.2*0.8 + 2.3*1.7 + 3.4*2.6 = 14.91
print(abs(v1))        # sqrt(1.2^2 + 2.3^2 + 3.4^2) ≈ 4.28
print(round(v1, 1))   # Vector(1.2, 2.3, 3.4)
Python 的特殊方法是实现强大、灵活类设计的关键:
- 运算符重载:通过 __add__,__sub__等实现自然语法
- 行为定制:使用 __str__,__len__等定制对象行为
- 高级特性:利用 __call__,__enter__等实现高级模式
- 编程建议:保持一致性、避免常见陷阱、优化性能
一些要点:
- 上下文管理方法(__enter__/__exit__)简化资源管理
"Python 的特殊方法是语言灵活性的核心体现。它们让开发者能够创建与内置类型无缝集成的自定义类型,使代码更加简洁、自然和表达力强。"
阅读原文:原文链接
该文章在 2025/7/18 10:35:21 编辑过