定制类

Python 特殊方法(Magic Methods)教程

在 Python 中,以双下划线开头和结尾的方法(如 __init____str__ 等)被称为"魔法方法"或"特殊方法"。这些方法允许我们自定义类的行为,使其能够更好地融入 Python 的生态系统。本文将详细介绍 Python 中常用的特殊方法及其应用场景。

1. 对象表示方法

__str____repr__

这两个方法用于对象的字符串表示:

class Student:
    def __init__(self, name):
        self.name = name
        
    def __str__(self):
        return f'Student object (name: {self.name})'
    
    __repr__ = __str__  # 通常两者内容相同,可以这样简写
  • __str__: 面向用户的友好字符串表示,print(obj)str(obj) 时调用
  • __repr__: 面向开发者的精确表示,在交互式环境直接输入变量名时调用

最佳实践:

  • __repr__ 的输出应尽可能包含重建对象所需的信息
  • 如果只实现一个,优先实现 __repr__,因为 __str__ 会回退到 __repr__

2. 迭代器协议

__iter____next__

使对象可迭代:

class Fibonacci:
    def __init__(self, max_num=100000):
        self.a, self.b = 0, 1
        self.max_num = max_num
        
    def __iter__(self):
        return self  # 实例本身就是迭代器
    
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > self.max_num:
            raise StopIteration()
        return self.a

使用:

for num in Fibonacci():
    print(num)

3. 序列和映射协议

__getitem__, __setitem__, __delitem__

使对象支持下标访问:

class ImprovedFibonacci:
    def __getitem__(self, n):
        if isinstance(n, int):  # 单个索引
            a, b = 1, 1
            for _ in range(n):
                a, b = b, a + b
            return a
        elif isinstance(n, slice):  # 切片
            start = n.start or 0
            stop = n.stop
            step = n.step or 1
            a, b = 1, 1
            result = []
            for i in range(stop):
                if i >= start and (i - start) % step == 0:
                    result.append(a)
                a, b = b, a + b
            return result
        else:
            raise TypeError("Indices must be integers or slices")

现代 Python 中,还可以实现 __len__ 来支持 len() 函数:

def __len__(self):
    return len(list(self))  # 假设对象可转换为列表

4. 属性访问控制

__getattr____setattr__

动态处理属性访问:

class DynamicAttributes:
    def __init__(self):
        self._data = {}
        
    def __getattr__(self, name):
        if name in self._data:
            return self._data[name]
        raise AttributeError(f"'DynamicAttributes' has no attribute '{name}'")
        
    def __setattr__(self, name, value):
        if name == '_data':
            super().__setattr__(name, value)
        else:
            self._data[name] = value

现代 Python 中,还可以使用 __getattribute__(所有属性访问都会调用它)和 property 装饰器来实现更精细的属性控制。

5. 可调用对象

__call__

使实例可以像函数一样被调用:

class CallableLogger:
    def __init__(self, prefix):
        self.prefix = prefix
        
    def __call__(self, message):
        print(f"{self.prefix}: {message}")

使用:

logger = CallableLogger("DEBUG")
logger("This is a debug message")

检查对象是否可调用:

callable(logger)  # 返回 True

6. 上下文管理器协议

__enter____exit__

使对象支持 with 语句:

class ManagedResource:
    def __enter__(self):
        print("Acquiring resource")
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Releasing resource")
        if exc_type is not None:
            print(f"Exception occurred: {exc_val}")
        return False  # 不抑制异常

使用:

with ManagedResource() as resource:
    # 使用资源
    pass

7. 现代 Python 新增的特殊方法

Python 3.x 引入了一些新的特殊方法:

__bytes__

定义对象如何转换为字节序列

__format__

定义对象如何格式化输出

__class_getitem__ (Python 3.7+)

支持泛型类型提示

最佳实践

  1. 一致性:特殊方法的实现应与 Python 内置类型的行为一致
  2. 文档:为特殊方法添加文档字符串,说明其行为
  3. 性能:特殊方法常被频繁调用,应保持高效
  4. 异常处理:合理处理边界情况和异常输入
  5. 组合优于继承:考虑使用组合而非继承来复用功能

总结

Python 的特殊方法提供了强大的类定制能力。通过合理实现这些方法,我们可以创建行为类似于内置类型的自定义类,使代码更加直观和 Pythonic。随着 Python 的发展,特殊方法也在不断演进,建议定期查阅官方文档以获取最新信息。