侧边栏壁纸
  • 累计撰写 24 篇文章
  • 累计创建 14 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Python特殊属性与方法

whfree
2026-01-10 / 0 评论 / 1 点赞 / 3 阅读 / 216789 字

Python特殊属性与方法

在Python中,类可以通过定义具有特殊名称的方法来实现某些由特殊语法调用的操作(例如算术运算、下标和切片),这些通常被称为魔术方法(Magic Methods),一般以双下划线为名(如__init____call__)。

它们无需显式调用,却能精准响应对象生命周期中的每一个关键事件——从创建、运算到销毁,魔术方法的身影无处不在。 这些方法的核心价值在于实现与Python原生语法的无缝融合。例如,当执行obj1 + obj2时,实则是__add__方法在幕后重载运算符;当使用print(obj)时,是__str__定义着对象的可视化形态。通过定制这些方法,开发者能将自定义类变得如内置类型般自然,能够创造出支持上下文管理、迭代协议等高级特性的对象。 理解魔术方法,不仅是掌握Python面向对象编程精髓的关键,更是解锁诸如单例模式、装饰器设计等进阶技巧的必经之路。它们如同乐高积木的接口,让代码在简洁优雅中迸发无限可能。本文将总结大部分魔术方法的功能和使用方式。

一些魔术方法在不同的Python版本经过修改,有些是在特定版本之后才添加,以下代码示例均使用Python 3.10运行。

基本自定义方法

object.__new__(cls[, ...])

功能特性

__new__是Python面向对象编程中控制实例创建过程的核心魔术方法,在__init__之前执行。其核心特性与用法如下:

  1. 实例化入口:__new__是静态方法(无需显式声明),负责创建并返回实例对象。若未显式定义,默认调用父类的__new__
  2. 控制实例化:通过返回不同类的实例,可动态决定对象类型,如果该方法没有返回实例或返回实例不是当前类实例,则会跳过当前类的__init__初始化方法。
  3. 不可变类型定制:继承不可变类型(如strtuple)时,必须在__new__中完成初始化操作。

应用示例

单例模式实现

通过__new__控制类仅生成唯一实例:

class SingletonObj:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            # 还未创建过实例对象则调用父类创建实例
            cls._instance = super().__new__(cls)
        # 返回已存在的实例
        return cls._instance

    def __init__(self, name):
        print("初始化", name)
        self.name = name

s1 = SingletonObj("Alpha")
s2 = SingletonObj("Beta")
# 创建的两个对象其实是同一个,但还是会进行多次初始化
# 如果需要只初始化一次可以在`__init__`方法中再进行判断是否初始化
print(s1 is s2)
print(s1.name)

# Output
# 初始化 Alpha
# 初始化 Beta
# True
# Beta
构建元类

python中本质是通过type创建类的,再通过类创建实例对象。type经常用来判断对象是属于什么类,但也可以用来动态创建一个类:type(name, bases, dict),参数含义如下:

  • name:字符串,表示要创建的类的名称
  • bases:元组,要创建的类的基类,多个表示多继承
  • dict:字典,要创建的类的属性

元类可以让开发者定制化的控制生成的类的特性,功能很强大,典型应用场景有:

  • 自动化类管理(如ORM框架)
  • 接口规范强制约束,如子类必须实现指定方法

但注意元类要避免滥用,元类的动态逻辑可能增加类定义时的开销,对性能有一定影响,复杂的元类还可能造成代码难以理解

下面以创建动物元类作为例子:

def method_upgrade(target_obj, up: int = 1):
    print(f"升{up}级")
    target_obj.level += up


class AnimalMeta(type):
    def __new__(cls, name, bases, attr):
        # 构建动物元类
        # 赋予动物等级属性,默认给类添加升级方法
        if 'level' not in attr:
            # 默认等级1级
            attr['level'] = 1
        if 'upgrade' not in attr:
            attr['upgrade'] = method_upgrade
        return super().__new__(cls, name, bases, attr)


class Animal_1(object, metaclass=AnimalMeta):
    ...


a = Animal_1()
print(a.level)
a.upgrade(3)
print(a.level)

# Output
# 1
# 升3级
# 4
动态对象工厂

能够从对象的基类中能够动态创建不同类的实例:

class DynamicAnimal:
    def __new__(cls, type_, *args, **kwargs):
        if type_ == "dog":
            return Dog(*args, **kwargs)
        elif type_ == "cat":
            return Cat(*args, **kwargs)
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print("非当前类实例不会执行")


class Dog:
    def __init__(self, name):
        self.name = name
        print("实例化Dog")

    def sound(self):
        return f"{self.name}: Woof!"


class Cat:
    def __init__(self, name):
        self.name = name
        print("实例化Cat")

    def sound(self):
        return f"{self.name}: Meow!"


a1 = DynamicAnimal("dog", name='dd')
a2 = DynamicAnimal("cat", name='cc')
a3 = DynamicAnimal('Other')
print(a1.sound())
print(a2.sound())

# Output
# 实例化Dog
# 实例化Cat
# 非当前类实例不会执行
# dd: Woof!
# cc: Meow!

注意事项

  1. 避免递归调用:在__new__中不能直接调用cls.__new__(),否则会导致死循环。应通过super()调用父类方法。
  2. __init__的协作:若__new__返回非当前类实例,当前类的__init__不会执行。
  3. 线程安全:单例模式需考虑多线程并发问题,可通过加锁或模块导入方式优化

object.__init__(self[, ...])

__init__是Python面向对象编程中对象初始化的核心方法,在__new__创建实例后自动调用。其核心特性与用法如下:

功能特性

  1. 初始化入口:负责设置实例的初始属性值,无返回值(隐式返回None)。
  2. 参数传递:接收__new__返回的实例(self)及构造函数参数,完成属性绑定。
  3. 继承协作:子类需通过super().__init__()显式调用父类初始化方法,确保继承链完整。

应用示例

基础属性初始化

为实例添加基础属性并赋予初始值:

class User:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        self.is_active = True  # 默认属性

user = User("Alice", 30)
print(user.is_active)  # 输出: True
继承中的层级初始化

子类继承时控制多父类的初始化顺序:

class Animal:
    def __init__(self, species):
        self.species = species

class Pet:
    def __init__(self, name):
        self.name = name

class Dog(Animal, Pet):
    def __init__(self, name, breed):
        Animal.__init__(self, "Canine")  # 显式调用父类
        Pet.__init__(self, name)         # 避免使用super时的MRO问题
        self.breed = breed

dog = Dog("Buddy", "Golden Retriever")
print(f"{dog.name} ({dog.species})")  # 输出: Buddy (Canine)

注意事项

  • 不要返回任何值,否则会抛出TypeError
  • 主要用于实例变量的初始化

object.__del__(self)

__del__是Python中用于对象销毁的魔术方法,负责在对象生命周期结束时执行清理操作。其核心特性与典型应用如下:

功能特性

  1. 触发时机:
    • 当对象的引用计数归零时自动调用(如手动del删除所有引用或程序退出时)。
    • 若存在循环引用,需依赖垃圾回收(GC)机制触发,但不可预测具体时间。
  2. 核心作用:释放对象占用的资源(如文件句柄、网络连接)或执行必要清理操作。
  3. 非析构函数:与C++不同,Python的__del__不保证立即执行,需依赖引用计数和GC机制。

应用示例

基础资源释放

关闭文件或数据库连接:

class FileHandler:
    def __init__(self, filename):
        self.file = open(filename, 'r')  # 打开文件
    
    def __del__(self):
        self.file.close()               # 确保文件关闭
        print("文件资源已释放")

handler = FileHandler("data.txt")
del handler  # 触发__del__,关闭文件

说明:即使未显式调用close()__del__会在对象销毁时关闭文件 。

引用计数验证

验证引用计数对__del__的影响:

class User:
    def __del__(self):
        print("对象被销毁")

# 场景1:单一引用
u1 = User()
del u1  # 输出:对象被销毁

# 场景2:多个引用
u2 = User()
u3 = u2
del u2  # 引用计数减1,未触发__del__
del u3  # 引用计数归零,输出:对象被销毁

说明:仅当所有引用被删除时,__del__才会执行 。

循环引用问题

处理循环引用导致的内存泄漏:

import weakref

class Node:
    def __init__(self):
        self.parent = None
    
    def __del__(self):
        print("节点已销毁")

# 创建循环引用
n1 = Node()
n2 = Node()
n1.parent = n2
n2.parent = n1

# 即使删除引用,__del__也不会触发(需GC介入)
del n1, n2

# 使用弱引用避免循环
n3 = Node()
n4 = Node()
n3.parent = weakref.ref(n4)  # 弱引用不增加计数
del n3, n4  # 正常触发__del__

说明:循环引用需通过弱引用或手动断开来解决 。

注意事项

  1. 不可靠性:

    • __del__可能因程序崩溃或强制退出而未被调用,关键资源释放应使用with语句或上下文管理器。

    • 示例替代方案:

      class SafeFileHandler:
          def __enter__(self):
              self.file = open("data.txt", 'r')
              return self.file
          def __exit__(self, exc_type, exc_val, exc_tb):
              self.file.close()
      
      with SafeFileHandler() as f:  # 自动管理资源
          data = f.read()
      
  2. 执行顺序问题:若父类定义了__del__,子类需显式调用super().__del__()以确保完整清理。

  3. 性能影响:定义__del__的类会禁用Python的快速垃圾回收,可能影响程序性能

object.__repr__(self)

__repr__是Python中用于控制对象字符串表示的核心魔术方法,主要目标是提供精确且可复现的对象描述,通常用于调试和开发场景。以下是其核心特性与典型应用:

功能特性

  1. 触发时机:
    • 通过repr(obj)内置函数显式调用。
    • 在交互式环境中直接输入对象名时自动调用(如Jupyter或Python Shell)。
    • 若未定义__str__方法,print(obj)str(obj)也会默认调用__repr__
  2. 返回值要求:
    • 准确性:应返回能完整描述对象状态的字符串,理想情况下可通过eval(repr(obj))重建对象。
    • 格式规范:建议使用ClassName(attr1=value1, ...)的表达式形式,便于开发者直接复制到代码中。

应用示例

开发调试
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector<x={self.x}, y={self.y}>"

    def __str__(self):
        return f"Vector({self.x}, {self.y})"


v = Vector(3, 4)
print(v)         # 输出: Vector(3, 4)
print(str(v))    # 输出: Vector(3, 4)
print(repr(v))   # 输出: Vector<x=3, y=4>

注意事项

  1. 避免副作用:__repr__中不应包含修改对象状态的逻辑,仅用于描述。
  2. 性能优化:对于大型对象,可仅展示关键属性,避免生成过长的字符串。

object.__str__(self)

__str__是Python中用于定义对象用户友好字符串表示的魔术方法,其核心目标是为终端用户提供可读性强、易于理解的对象描述。以下是其核心特性和典型应用场景:

功能特性

  1. 触发时机:
    • 通过print(obj)str(obj)调用。
    • 若未定义__str__,Python会调用__repr__方法作为替代。
  2. 返回值要求:
    • 用户友好:避免技术细节,用自然语言描述对象核心属性。
    • 简洁性:通常比__repr__更简短,适合日志输出或界面展示

应用示例

用户友好的输出
class Player:
    def __init__(self, name, level, hp):
        self.name = name
        self.level = level
        self.hp = hp

    def __str__(self):
        return f"玩家:{self.name}(等级{self.level},生命值:{self.hp})"


player = Player("Bob", 15, 2000)
print(player)  # 输出:玩家:Bob(等级15,生命值:2000)

注意事项

  1. __repr__的协作:__str__未定义时,默认调用__repr__,建议同时定义两者
  2. 避免副作用:__str__中不应修改对象状态或执行复杂计算。
  3. 性能优化:对于属性较多的对象,可仅展示关键信息。

object.__bytes__(self)

__bytes__是Python中用于定义对象二进制字节表示的魔术方法,其核心目标是将对象转换为不可变的字节序列,适用于网络传输、文件存储或自定义协议等二进制处理场景。以下是其核心特性与典型应用解析:

功能特性

  1. 触发机制:

    • 通过bytes(obj)obj.__bytes__()调用,返回bytes类型对象。
    • 若未定义__bytes__方法,直接调用bytes(obj)可能引发TypeError
  2. 返回值要求:

    • 必须返回不可变的字节对象bytes类型),每个字节值在0 <= x < 256范围内。
    • 通常结合字符串编码(如UTF-8)或数值转换实现。
  3. __str__的区别:

    特性 __bytes__ __str__
    目标格式 二进制数据 人类可读的字符串
    典型应用 网络传输、文件存储 日志输出、界面展示

应用示例

自定义类的二进制序列化

将对象属性编码为字节流,可用于网络传输或持久化存储。

import struct


class Vector3D:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def __bytes__(self):
        # 使用struct模块打包数值为字节(大端序)
        header = struct.pack('>3I', self.x, self.y, self.z)
        return header

    @classmethod
    def from_bytes(cls, data):
        x, y, z = struct.unpack('>3I', data)
        return cls(x, y, z)

    def __repr__(self):
        return f"Vector3D(x={self.x}, y={self.y}, z={self.z})"


vec = Vector3D(10, 20, 30)
byte_data = bytes(vec)
print(byte_data)      # 输出: b'\x00\x00\x00\n\x00\x00\x00\x14\x00\x00\x00\x1e'
restored_vec = Vector3D.from_bytes(byte_data)
print(restored_vec)   # 输出: Vector3D(x=10, y=20, z=30)

说明:通过struct.pack将三个整数打包为12字节的二进制数据,支持反序列化还原对象。

注意事项

  1. 编码一致性:
    • 若需从字节重建对象,需确保编码与解码逻辑严格匹配(如字符编码、字节序)。
    • 推荐优先使用picklejson模块处理复杂对象的序列化。
  2. 性能优化:
    • 对于大规模数据,直接操作bytesbytearray比逐字符拼接更高效。
  3. 不可变特性:
    • bytes对象不可修改,需修改时使用bytearray

object.__format__(self, format_spec)

__format__是Python中用于自定义对象格式化输出的魔术方法,通过重写此方法可实现对象与format()函数、f-string等格式化工具的无缝协作。其核心特性与典型应用如下:

功能特性

  1. 触发机制:
    • 调用format(obj, format_spec)str.format()时,实际执行obj.__format__(format_spec)
    • 若未定义__format__,默认调用object.__format__,可能引发TypeError
  2. 参数控制:
    • format_spec:接收格式说明符(如"ymd"">20s"),根据需求解析并生成对应输出。
    • 支持标准格式语法(如对齐、精度)和自定义格式符号。

应用示例

自定义日期对象格式化方式
class Date:
    format_rules = {
        "ymd": "{year}-{month:>02}-{day:>02}",
        "dmy": "{day:>02}/{month:>02}/{year}",
        "mdy": "{month:>02}-{day:>02}-{year}"
    }

    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __format__(self, format_spec):
        # 默认格式为ymd
        fmt = self.format_rules.get(format_spec, self.format_rules["ymd"])
        return fmt.format(year=self.year, month=self.month, day=self.day)


d = Date(2025, 4, 14)
print(format(d, "ymd"))    # 输出:14/04/2025
print(f"{d:ymd}")          # 输出:04-14-2025

运算符魔术方法

Python通过魔术方法(如 __lt____le__ 等)支持自定义类的逻辑比较操作,使对象能够像内置类型一样使用运算符(如 <>=== 等)。以下是这些方法的核心用法及典型场景:

核心方法

魔术方法 运算符 功能描述
__lt__(self, other) < 判断 self 是否小于 other
__le__(self, other) <= 判断 self 是否小于或等于 other
__eq__(self, other) == 判断 self 是否等于 other
__ne__(self, other) != 判断 self 是否不等于 other
__gt__(self, other) > 判断 self 是否大于 other
__ge__(self, other) >= 判断 self 是否大于或等于 other

关键规则:

  1. 若未实现某方法(如 __gt__),Python会尝试反向方法(如 other.__lt__(self))。
  2. __eq____hash__ 需同步实现,否则对象无法作为字典键或集合元素

应用示例

# 经常需要将小于1的浮点数转换为百分比格式显示
# 这里就以定义一个Percent类作为例子
class Percent:
    def __init__(self, value, base=1, precision: int = 3):
        # 基数默认为1
        self.base = base
        self.value = self.value_transform(value, precision)

    def value_transform(self, v, precision: int = 3):
        # 数值转换,返回小于1的小数
        if isinstance(v, str) and v.endswith('%'):
            return round(float(v[:-1]) / 100, precision)
        else:
            return round(float(v) / self.base, precision)

    def __str__(self):
        return f'{self.value:.2%}'

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self.value:.2%})"

    # def __lt__(self, other):
    #     if isinstance(other, Percent):
    #         return self.value < other.value
    #     else:
    #         return self.value < other

    def __eq__(self, other):
        if isinstance(other, Percent):
            return self.value == other.value
        else:
            return self.value == other

    def __gt__(self, other):
        # 没有实现__lt__时,大小比较会使用此方法
        if isinstance(other, Percent):
            print(self.value, "greater than", other.value)
            return self.value > other.value
        else:
            print(self.value, "greater than", other)
            return self.value > other

    def __le__(self, other):
        if isinstance(other, Percent):
            return self.value <= other.value
        else:
            return self.value <= other

    def __ge__(self, other):
        if isinstance(other, Percent):
            return self.value >= other.value
        else:
            return self.value >= other

a = Percent(0.75)
b = Percent("35.236%")
c = Percent(59, base=100)
d = 0.75

print(a > b)
print(b < c)
print(a == d)

object.__hash__(self)

__hash__是Python中用于定义对象哈希值的魔术方法,其核心作用是为对象生成唯一的整数标识,支持在哈希表结构(如字典、集合)中高效存储和查找。以下是其核心用法及典型场景解析:

功能特性

  1. 触发机制:
    • 通过内置函数hash(obj)调用,返回整数哈希值。
    • 若未定义__hash__,默认继承object类的哈希实现(基于对象内存地址)。
  2. __eq__的强关联:
    • 等价性规则:若a == b为真,则hash(a) == hash(b)必须成立,否则哈希表逻辑会失效。
    • 同步实现:重写__eq__时需同时重写__hash__,否则对象无法作为字典键或集合元素。
  3. 不可变性要求:
    • 若对象属性可变,则不应实现__hash__,否则哈希值可能动态变化,导致哈希表存储错误

应用示例

可哈希的自定义对象
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        # 修改_p属性不会导致哈希值变化
        self._p = "y"

    def __eq__(self, other):
        # 比较对象必须也是Point实例
        return isinstance(other, Point) and self.x == other.x and self.y == other.y

    def __hash__(self):
        return hash((self.x, self.y))  # 元组哈希组合

    def __repr__(self):
        return f"Point({self.x}, {self.y})"


# 示例
p1 = Point(3, 4)
p2 = Point(3, 4)
print(hash(p1) == hash(p2))
my_dict = {p1: "坐标1"}
print(my_dict[p2])  # 输出:坐标1

# 如果对象的属性变化导致哈希值变化,会导致字典键失效
# p1.x = 2
不实现__eq__方法
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        # 修改_p属性不会导致哈希值变化
        self._p = "y"

    # def __eq__(self, other):
    #     # 比较对象必须也是Point实例
    #     return isinstance(other, Point) and self.x == other.x and self.y == other.y

    def __hash__(self):
        return hash((self.x, self.y))  # 元组哈希组合

    def __repr__(self):
        return f"Point({self.x}, {self.y})"


# 示例
p1 = Point(3, 4)
p2 = Point(3, 4)

my_dict = {p1: "坐标1"}
print(my_dict[p1])  # 输出:坐标1
# 没有实现__eq__方法无法以预期逻辑获取值,只有同一对象才能拿到值,原因在于默认的__eq__方法仅比较内存地址
print(my_dict[p2])  # KeyError: Point(3, 4)
__hash____eq__方法不等价
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        # 修改_p属性不会导致哈希值变化
        self._p = "y"

    def __eq__(self, other):
        # 比较对象必须也是Point实例
        return isinstance(other, Point) and self.x == other.x and self.y == other.y and self._p == other._p

    def __hash__(self):
        return hash((self.x, self.y))  # 元组哈希组合

    def __repr__(self):
        return f"Point({self.x}, {self.y})"


# 示例
p1 = Point(3, 4)
p2 = Point(3, 4)

my_dict = {p1: "坐标1"}

print(my_dict[p1])           # 输出:坐标1
print(my_dict[Point(3, 4)])  # 输出:坐标1

p2._p = "w"
# 尽管哈希值一致但键比较为False
print(my_dict[p2])           # KeyError: Point(3, 4)

注意事项

  1. 哈希表的工作原理与哈希值的作用
    哈希表通过__hash__方法计算键的哈希值,并将数据存储在对应哈希桶(数组索引)中。查找时,哈希表会重新计算键的哈希值,定位到对应的桶,再通过__eq__方法精确匹配具体条目。

    • 哈希值:决定数据在哈希表中的存储位置。
    • 等价性:哈希值相同且__eq__返回True时,才视为同一键
  2. __hash__方法的核心价值在于:

    • 哈希表兼容:支持对象作为字典键或集合元素。
    • 性能优化:通过唯一哈希值实现快速查找(O(1)时间复杂度)。
    • 数据完整性:防止重复数据污染集合或字典。
  3. 哈希生成策略:

    • 组合哈希:将多个属性打包为元组后取哈希(推荐)。
    • 数值处理:避免直接哈希浮点数(可能存在精度差异),可转为整数。

object.__bool__(self)

__bool__是Python中用于自定义对象布尔值判断逻辑的魔术方法,其核心作用是控制对象在条件判断(如if语句)或bool()函数调用时的真假值。以下是其核心特性、用法及典型场景的总结:

功能特性

  1. 触发时机:
    • 当调用bool(obj)或对象参与逻辑判断(如if obj)时触发。
    • 若未定义__bool__,Python会尝试调用__len__方法:返回0则视为False,否则为True
    • 若两者均未定义,默认返回True(如空类实例)。
  2. 返回值要求:
    • 必须返回布尔类型(TrueFalse),否则会抛出TypeError
  3. 优先权:
    • __bool__优先级高于__len__。若同时定义,仅__bool__生效

应用示例

自定义真值判断
class User:
    def __init__(self, age):
        self.age = age

    def __bool__(self):
        return self.age >= 18


adult = User(20)
child = User(16)
print(bool(adult))  # True
print(bool(child))  # False
if adult:
    print("成年人")  # 输出:成年人

小结

魔术方法 功能 应用
object.__new__(cls[, ...]) 创建对象实例,在__init__之前调用,控制对象生成过程 实现单例模式、继承不可变类型(如str/tuple)时定制构造逻辑
object.__init__(self[, ...]) 初始化对象属性,在实例创建后调用 通用属性初始化(如类成员变量赋初始值)
object.__del__(self) 对象销毁前调用,用于资源清理 关闭文件句柄、释放网络连接等(注:不可靠,依赖垃圾回收机制)
object.__repr__(self) 返回开发者可读的字符串表示,用于调试 输出类似ClassName(attr1=value1, ...)的明确对象信息
object.__str__(self) 返回用户友好的字符串表示,优先于__repr__print()调用 定义对象对外显示内容(如datetime对象的人性化时间格式)
object.__bytes__(self) 返回对象的字节序列表示 序列化对象为字节流(如自定义二进制协议的数据封装)
object.__format__(self, format_spec) 定义对象格式化输出,支持format()函数和f-string 定制不同格式符的显示(如"${:.2f}"格式化为货币金额)
__lt__(self, other) 定义 < 运算符行为,返回布尔值 对象比较排序(如按学生成绩排序)、集合去重等场景
__le__(self, other) 定义 <= 运算符行为 同上
__eq__(self, other) 定义 == 运算符行为 判断对象等价性(如判断两个用户对象的ID是否相同)
__ne__(self, other) 定义 != 运算符行为 同上
__gt__(self, other) 定义 > 运算符行为 同上
__ge__(self, other) 定义 >= 运算符行为 同上
object.__hash__(self) 返回对象的哈希值,影响作为字典键的行为 自定义对象作为字典键时的哈希逻辑(需与__eq__方法逻辑一致)
object.__bool__(self) 定义对象的布尔值,控制if objwhile obj等逻辑判断 判断对象是否为“真”(如空集合返回False,非空返回True

自定义属性访问

object.__getattr__(self, name)

__getattr__是Python中用于动态处理属性访问的魔术方法,当尝试访问对象中不存在的属性时触发。其核心作用是为属性访问提供兜底逻辑,适用于动态属性生成、延迟加载或错误处理等场景。以下是其核心用法及代码示例:

功能特性

  1. 触发条件:

    • 仅在访问不存在的属性时触发(包括未定义的属性或被__delattr__删除的属性)。
    • 若属性存在于实例、类或父类中,__getattr__不会被调用。
  2. 方法签名:

    def __getattr__(self, name: str) -> Any
    
    • name:被访问的属性名称(字符串形式)。
  3. 默认行为:

    • 若未定义__getattr__,访问不存在的属性会抛出AttributeError

应用示例

class DynamicDict:
    def __init__(self, data):
        self.__dict__["_data"] = data  # 避免__setattr__覆盖

    def __getattr__(self, key):
        if key in self._data:
            return self._data[key]
        raise AttributeError(f"属性 {key} 不存在")


# 示例
user_data = {"name": "Alice", "age": 30}
user = DynamicDict(user_data)
print(user.name)   # 输出: Alice
print(user.age)    # 输出: 30
print(user.email)  # 抛出: AttributeError

注意事项

  1. 避免递归调用:

    • __getattr__中避免再次访问不存在属性,否则会触发死循环。
    class BadExample:  
        def __getattr__(self, name):  
            return self.name   # 错误!再次触发__getattr__
    
  2. __getattribute__的区别:

    • __getattribute__:所有属性访问均触发,无论属性是否存在。
    • 若需拦截所有属性操作,优先使用__getattribute__,但需注意避免递归。
  3. 性能优化:

    • 高频访问不存在的属性可能影响性能,建议结合缓存机制(如__dict__直接赋值)

object.__getattribute__(self, name)

__getattribute__是Python中用于拦截所有属性访问的魔术方法,无论属性是否存在,只要通过实例对象访问属性(包括类属性和实例属性),均会触发该方法。以下是其核心特性与典型应用场景的总结:

功能特性

  1. 触发机制:

    • 通过实例访问任何属性时触发,无论属性是否存在。

    • 若未定义__getattribute__,Python默认调用父类(object)的__getattribute__方法。

    • __getattr__的区别:

      特性 __getattribute__ __getattr__
      触发条件 所有属性访问 仅访问不存在属性时触发
      优先级 高(优先调用) 低(仅当__getattribute__未处理时触发)
      返回值 必须返回属性值 可返回默认值或自定义逻辑
  2. 方法签名:

    def __getattribute__(self, name: str) -> Any
    
    • name为属性名称字符串。
  3. 默认行为:

    • 若未重写,默认返回父类object.__getattribute__(self, name)的结果。

应用示例

class LoggedAccess:
    def __init__(self, name):
        self.name = name
        self.value = 100

    def __getattribute__(self, attr):
        print(f"访问属性:{attr}")
        # 必须通过父类方法获取属性,否则递归
        return object.__getattribute__(self, attr)

obj = LoggedAccess("测试")
print(obj.name)    # 输出:访问属性:name → 测试
print(obj.value)   # 输出:访问属性:value → 100

注意事项

  1. 避免递归陷阱

    • __getattribute__禁止直接使用self.属性,否则会无限递归。
    • 正确做法:通过object.__getattribute__(self, attr)super().__getattribute__(attr)访问属性。

    错误示例

    class BadExample:  
        def __getattribute__(self, attr):  
            return self.attr  # 触发无限递归
    
  2. __getattr__的协作:

    • 若需触发__getattr__,需在__getattribute__中手动抛出AttributeError

      class Hybrid:  
          def __getattribute__(self, attr):  
              try:  
                  return object.__getattribute__(self, attr)  
              except AttributeError:  
                  return f"默认值:{attr}"  # 调用__getattr__
      
  3. 性能影响:

    • 高频属性访问可能因拦截逻辑导致性能下降,建议仅在必要时使用。

小结

__getattribute__方法的核心应用场景包括:

  1. 日志与监控:记录属性访问行为。
  2. 动态数据处理:加密、格式化或权限校验。
  3. 代理与委托:实现复杂对象的属性代理

object.__setattr__(self, name, value)

__setattr__是Python中用于拦截对象属性赋值操作的魔术方法,其核心作用是在属性被赋值时执行自定义逻辑(如验证、代理或动态计算)。以下是其核心用法及典型场景的总结:

功能特性

  1. 触发机制:
    • 通过obj.attr = valuesetattr(obj, 'attr', value)触发。
    • 拦截所有属性赋值(包括已存在和新增属性)。
  2. 与默认行为的区别:
    • 若未定义__setattr__,Python默认将属性存入实例的__dict__字典。
    • 若自定义__setattr__,需手动调用父类方法或操作__dict__来存储属性,否则赋值失效。
  3. 递归陷阱:
    • __setattr__中直接使用self.attr = value会触发无限递归(需通过object.__setattr__self.__dict__绕过)。

应用示例

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __setattr__(self, name, value):
        print("setattr", name, value)
        if name == "age" and not isinstance(value, int):
            raise ValueError("年龄必须是整数")
        # 通过父类方法赋值或使用__dict__赋值
        super().__setattr__(name, value)
        # 或
        # self.__dict__[name] = value
        # 触发无限递归
        # setattr(self, name, value)
        # self.name = value

    def __repr__(self):
        return f"Person({self.name}, {self.age})"


p = Person("Jack", 23)
p.name = "Lily"
try:
    p.age = "invalid"
except ValueError as e:
    print(e)

# 输出:
# setattr name Jack
# setattr age 23
# setattr name Lily
# setattr age invalid
# 年龄必须是整数

object.__delattr__(self, name)

__delattr__是Python中用于拦截属性删除操作的魔术方法,其核心作用是在删除对象属性时执行自定义逻辑(如资源清理、状态验证或权限控制)。以下是其核心用法及典型场景的总结:

功能特性

  1. 触发机制:
    • 通过del obj.attrdelattr(obj, 'attr')触发。
    • 若未定义__delattr__,Python默认通过object.__delattr__删除属性(从__dict__中移除)。
  2. delattr()函数的关联:
    • delattr(obj, 'attr')等价于调用obj.__delattr__('attr')
  3. 递归陷阱:
    • __delattr__中直接使用del self.attr会触发无限递归(需通过object.__delattr__或操作__dict__绕过)。

应用示例

基础属性删除

场景:删除实例属性时记录日志。

class User:
    def __init__(self, name):
        self.name = name
        self.session_token = "token_123"

    def __delattr__(self, name):
        print(f"delattr {name}")
        super().__delattr__(name)  # 调用父类方法删除
        # 或从__dict__删除
        # self.__dict__.pop(name)
        # 以下方式会造成无限递归
        # delattr(self, name)


user = User("Alice")
del user.name  # 输出:删除属性:name → 属性被删除
print(hasattr(user, "name"))  # 输出:False
删除类属性

场景:删除类属性。

class Config:
    debug_mode = True
    version = "1.0"

    @classmethod
    def __delattr__(cls, name):
        print(f"delattr: {name}")
        super().__delattr__(name)


delattr(Config, "debug_mode")         # 输出:删除类属性:debug_mode
print(hasattr(Config, "debug_mode"))  # 输出:False

object.__dir__(self)

__dir__是Python中用于自定义对象属性与方法列表的魔术方法,其核心作用是通过重写该方法,控制dir(obj)obj.__dir__()的输出内容。以下是其核心用法及典型场景的总结:

功能特性

  1. 触发机制:
    • 调用dir(obj)时,实际执行obj.__dir__()方法,返回属性与方法名的列表。
    • 若未定义__dir__,默认返回对象所有属性(包括实例属性、类属性和父类继承的属性和方法)的有序列表。
  2. dir()函数的关系:
    • dir(obj)会对__dir__返回的结果进行排序,而直接调用obj.__dir__()则保持原始顺序。
  3. 应用场景:
    • 动态生成属性列表(如排除私有属性或添加动态计算属性)。
    • 调试时快速查看对象结构。

应用示例

class CustomUser:
    def __init__(self, name):
        self.name = name
        self._password = "******"  # 私有属性
        self.__uid = "1234"

    @property
    def uid(self):
        return self.uid

    def _other_method(self):
        ...

    def hello2(self):
        ...

    def hello1(self):
        ...

    def __dir__(self):
        # 返回类、实例的属性和方法,过滤掉下划线开头的属性、方法
        return sorted(
            [attr for attr in self.__dict__ if not attr.startswith("_")] + \
            [attr for attr in self.__class__.__dict__ if not attr.startswith("_")]
        )


custom_user = CustomUser("Bob")
print(dir(custom_user))  # 输出: ['hello1', 'hello2', 'name', 'uid']

注意事项

  1. 性能优化:
    • 避免在__dir__中执行复杂计算,否则频繁调用dir()可能影响性能。
  2. __dict__的区别:
    • __dict__仅包含实例属性字典,而__dir__可包含动态生成的属性名和继承的方法。
  3. 调试友好性:
    • 重写__dir__时建议保留核心属性和方法,便于开发者快速理解对象结构

属性描述符

首先理解什么是属性描述符,描述符是带有“绑定行为”的对象属性,它的属性访问已经被描述符协议中的方法覆盖了。这些方法是__get____set____delete__。如果一个对象定义了这些方法中的任何一个,它就是一个描述符。

默认的属性操作是从对象的字典中获取(get)、设置(set)或者删除(delete)属性;例如a.x的查找顺序是: a.x➡️a.__dict__['x']➡️type(a).__dict__['x']➡️type(a)的基类(不包括元类),如果查找的值是对象定义的描述方法之一,python就会调用描述符方法来重载默认行为,对属性如何操作取决于定义了哪些描述符方法。

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)。它广泛用于数据验证、动态计算、延迟加载等场景,是@property装饰器的底层实现基础。

描述符协议与分类

属性描述符分为两类:

  • 数据描述符:实现__set____delete__方法(或两者)。优先级高于实例属性,强制控制属性的读写逻辑。
  • 非数据描述符:仅实现__get__方法,可被实例属性覆盖。

应用示例

典型场景:类型检查与数据验证

class Descriptor:
    def __init__(self, name, type_):
        self.name = name
        self.type = type_

    def __get__(self, instance, owner):
        print('Descriptor get', instance, owner)
        return instance.__dict__.get(self.name, None)

    def __set__(self, instance, value):
        print('Descriptor set', instance, value)
        if not isinstance(value, self.type):
            raise TypeError(f"{self.name} 必须是 {self.type.__name__} 类型")
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('Descriptor delete', instance)
        try:
            instance.__dict__.pop(self.name)
        except KeyError:
            ...


class Person:
    age = Descriptor("age", int)
    name = Descriptor("name", str)

    def __repr__(self):
        return f"Person({self.__dict__.get('age')}, {self.__dict__.get('name')})"


p = Person()
print(p.name)
p.name = 'jack'
print(p.name)
del p.name
print(p.name)

# 输出
# Descriptor get Person(None, None) <class '__main__.Person'>
# None
# Descriptor set Person(None, None) jack
# Descriptor get Person(None, jack) <class '__main__.Person'>
# jack
# Descriptor delete Person(None, jack)
# Descriptor get Person(None, None) <class '__main__.Person'>
# None

注意事项

  1. 优先级规则:

    • 数据描述符优先级高于实例属性,非数据描述符反之。例如:

      class Desc:
          def __get__(self, inst, cls):
              return "Desc"
      
      class A:
          x = Desc()
      
      a = A()
      print(a.x)     # 输出:Desc
      a.x = "prop"   # 非数据描述符会被覆盖
      print(a.x)     # 输出:prop
      
  2. 避免递归:

    • __set__中避免直接使用instance.attr = value,应操作__dict__
  3. 自动捕获属性名:

    • 使用__set_name__(self, owner, name)自动获取属性名(Python 3.6+):

小结

属性描述符通过协议方法实现灵活的属性控制逻辑,适用于:

  • 数据验证(如类型、范围校验)
  • 动态计算(如ORM字段映射、公式计算)
  • 资源管理(如延迟加载、缓存优化)
  • 方法绑定(如装饰器实现)

推荐实践:优先使用@property简化简单场景,复杂逻辑或复用需求使用显式描述符类。

object.__objclass__

属性 __objclass__ 会被 inspect 模块解读为指定此对象定义所在的类(正确设置此属性有助于动态类属性的运行时内省)。对于可调用对象来说,它可以指明预期或要求提供一个特定类型(或子类)的实例作为第一个位置参数(例如,CPython 会为实现于 C 中的未绑定方法设置此属性)。

按照解释来说应该不是用户使用的,也未见相关使用示例。

object.__slots__

__slots__是Python中用于优化内存占用和提升属性访问速度的特殊类属性。通过显式声明允许的实例属性,它能够限制动态属性的添加,并替代默认的__dict__字典存储方式。以下是其核心特性、典型应用场景及代码示例:

功能特性

  1. 内存优化:
    • 实例不再使用__dict__字典存储属性,改用固定大小的数组,减少内存碎片和冗余空间。
  2. 加速属性访问:
    • 属性直接通过数组偏移量访问,无需哈希查找,访问速度提升约20-30%。
  3. 限制动态属性:
    • 实例只能拥有__slots__中声明的属性,禁止动态添加其他属性。

应用示例

场景:限制属性和内存优化

class RegularUser:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.email = None
        self.sex = 1


class SlotUser:
    __slots__ = ('name', 'age')

    def __init__(self, name, age):
        self.name = name
        self.age = age


# 动态属性测试
user_regular = RegularUser("Alice", 25)
user_regular.email = "alice@example.com"
print(user_regular.__dict__)  # 输出: {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}


user_slot = SlotUser("Bob", 30)
try:
    user_slot.email = "bob@example.com"   # 触发 AttributeError
except AttributeError as e:
    print(e)    # 输出: 'SlotUser' object has no attribute 'email'

性能测试:属性访问速度

from timeit import timeit


class TestA:
    __slots__ = ['x']


class TestB:
    pass


# 访问速度测试
a = TestA()
b = TestB()


def test_slots():
    a.x = 1
    _ = a.x


def test_dict():
    b.x = 1
    _ = b.x


print(f"Slots类耗时: {timeit(test_slots, number=10_000_000):.4f}秒")
print(f"普通类耗时: {timeit(test_dict, number=10_000_000):.4f}秒")
# Slots类耗时: 0.7755秒
# 普通类耗时: 0.9345秒

注意事项

场景 推荐使用 不建议使用
需要创建大量实例(10万+)
实例属性固定且数量少(<10)
需要动态添加属性
使用弱引用(weakref ✅(需额外声明__weakref__

注意:

  1. 不可动态添加属性:所有可能属性需预先声明。
  2. 继承链处理:子类必须显式定义__slots__
  3. __dict__冲突:若需要动态属性,需在__slots__中显式包含'__dict__'

小结

__slots__适用于内存敏感的场景(如数据处理、游戏开发),但需权衡灵活性的损失。其核心价值在于:

  • 内存优化:通过固定内存布局减少冗余。
  • 性能提升:属性访问速度接近C结构体。
  • 代码规范:强制显式声明属性,提升可维护性。

推荐实践:在需要处理海量数据或高频访问属性的场景下优先使用,但避免在需要动态扩展属性的类中使用。

小结

魔术方法 功能 应用
object.__getattr__(self, name) 当访问不存在的属性时触发 惰性加载、动态生成缺失属性、错误处理
object.__getattribute__(self, name) 拦截所有属性访问,无论属性是否存在 全局属性访问日志、权限控制、属性代理
object.__setattr__(self, name, value) 拦截属性赋值操作 数据验证(类型/范围)、属性关联计算、代理赋值
object.__delattr__(self, name) 拦截属性删除操作 防止关键属性被删除、资源释放(如关闭文件句柄)
object.__dir__(self) 控制dir(obj)的输出列表 隐藏内部属性、动态生成属性列表、调试辅助
object.__get__(self, instance, owner=None) 描述符协议:获取属性值 动态计算属性值(如ORM字段)、延迟加载、方法绑定
object.__set__(self, instance, value) 描述符协议:设置属性值 数据校验(类型/范围)、属性联动更新(如设置半径后自动计算面积)
object.__delete__(self, instance) 描述符协议:删除属性值 资源清理(如关闭网络连接)、权限控制
object.__objclass__ 指定对象定义所在的类 -
object.__slots__ 限制实例属性并优化内存/访问速度(替代__dict__ 高频创建海量实例(如数据处理)、性能敏感场景(如游戏开发)

自定义类创建

object.__init__subclass__(cls)

__init_subclass__是Python 3.6引入的特殊方法,用于在子类被定义时自动触发自定义逻辑,替代元类编程的复杂场景。它允许父类在子类继承时动态修改子类行为,例如注册子类、验证属性或注入通用逻辑。以下是其核心用法及典型应用场景:

功能特性

  1. 触发时机:
    • 当子类继承父类时自动调用,无需显式调用或实例化
    • 例如:父类Base定义此方法后,任何继承Base的子类(如SubClass)在定义时会触发__init_subclass__
  2. 参数说明:
    • cls:当前正在定义的子类(非父类)。
    • **kwargs:子类继承时传递的关键字参数(如class SubClass(Base, param=value)中的param=value)。
  3. 与元类的区别:
    • 元类(metaclass)控制类的创建过程,而__init_subclass__作用于已创建的子类,更轻量且易用

应用示例

子类注册与追踪

场景:父类自动记录所有子类,用于动态管理或插件系统。

class Base:
    subclasses = []
    
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Base.subclasses.append(cls)  # 将子类添加到列表

class SubClass1(Base):
    pass

class SubClass2(Base):
    pass

print(Base.subclasses)  
# 输出: [<class '__main__.SubClass1'>, <class '__main__.SubClass2'>]

说明:通过__init_subclass__实现子类自动注册,无需手动管理。

动态注入属性

场景:父类为所有子类添加通用属性。

class SuperClass:
    def __init_subclass__(cls, /, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.default_name = default_name
        print("init_subclass", default_name, kwargs)


class SuperClass1(SuperClass, default_name="SubClass1"):
    pass


class SuperClass2(SuperClass, default_name="SubClass2"):
    pass


print(SuperClass1.default_name)
print(SuperClass2.default_name)
# 输出
# init_subclass SubClass1 {}
# init_subclass SubClass2 {}
# SubClass1
# SubClass2

说明:通过子类继承时的关键字参数传递,父类统一管理属性。注意父类默认是object,它的__init_subclass__不接受额外的关键字参数,否则会报错。

注意事项

  1. 参数传递:
    • 若父类__init_subclass__需要参数,子类继承时需显式传递(如class Sub(Base, param=value))。
    • 剩余参数需通过super().__init_subclass__(**kwargs)传递给父类,注意默认object对象没有其余参数。
  2. 与元类的协作:
    • __init_subclass__在元类type.__new__()之后执行,因此不能修改类的创建过程(如动态添加元类方法)。
  3. 性能影响:
    • 高频创建子类时需谨慎使用,避免复杂逻辑影响性能。

object.__set_name__(self, owner, name)

__set_name__ 是 Python 3.6 引入的特殊方法,用于在描述符(Descriptor)被声明为类属性时自动捕获属性名称,解决了手动传递属性名的繁琐问题。以下是其核心用法及典型场景的总结:

功能特性

  1. 触发条件:
    • 当描述符类(实现 __get__/__set__/__delete__ 的类)被声明为另一个类的类属性时触发。
    • 参数说明:
      • self:描述符实例
      • owner:宿主类(包含该描述符的类)
      • name:描述符在宿主类中的属性名称
  2. 与元类的区别:
    • 元类控制类的创建过程,而__set_name__作用于已定义的宿主类,更轻量且易用。

应用示例

自动属性名绑定

功能:动态生成私有属性名,实现托管属性访问,通过 __set_name__ 自动生成私有属性名,避免硬编码。

class Descriptor:
    def __set_name__(self, owner, name):
        self.public_name = name          # 公开属性名(如"age")
        self.private_name = f"_{name}"   # 私有属性名(如"_age")

    def __get__(self, instance, owner):
        return getattr(instance, self.private_name)

    def __set__(self, instance, value):
        setattr(instance, self.private_name, value)


class Person:
    age = Descriptor()  # 自动触发 __set_name__


p = Person()
p.age = 30
print(p.age)         # 输出: 30
print(p.__dict__)    # 输出: {'_age': 30}
方法绑定与结果缓存
import time


class cached_property:
    def __init__(self, func):
        # 初始化传入的参数为可回调函数,可以使用@方式作为装饰器使用
        self.func = func

    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, instance, owner):
        # 缓存计算结果到实例字典
        # print("计算", self.func, instance)
        value = instance.__dict__.setdefault(self.name, self.func(instance))
        return value


class Data:
    @cached_property
    def prop1(self):
        print("计算prop1")
        time.sleep(5)
        return 'v1'

    @cached_property
    def prop2(self):
        print("计算prop2")
        time.sleep(5)
        return 'v2'


d = Data()
print(d.prop1)
print(d.prop2)
# 后续访问属性值将会直接返回结果

注意事项

  1. 参数传递:
    • 若描述符需要额外参数(如校验规则),需在__init__中定义,而__set_name__仅处理属性名。
  2. 继承处理:
    • 子类需显式调用super().__set_name__以支持多级继承。
  3. 性能优化:
    • 高频属性访问场景下,直接操作 __dict__getattr/setattr 更高效

object.__mro_entries__(self, bases)

__mro_entries__ 是 Python 3.7(PEP 560)引入的特殊方法,用于在类继承时动态替换基类。它主要服务于泛型编程和类型注解场景,允许开发者通过非类对象(如泛型参数化类型)作为基类时,将其转换为实际的类对象。以下是其核心用法及典型应用场景:

功能特性

  1. 功能定位:
    • 当类定义中的基类列表包含非类对象(如 list[int]、自定义泛型类型)时,Python 解释器会尝试调用该对象的 __mro_entries__ 方法,将其转换为有效的基类元组。
    • 该方法需返回一个元组,包含实际参与 MRO 计算的基类。
  2. 触发条件:
    • 仅在类继承声明中的基类不是 type 实例时触发。
    • 若基类已经是普通类(class 定义的类型),则不会调用此方法。
  3. 与 MRO 的关系:
    • 该方法在 MRO 计算前执行,用于修正基类列表,确保后续的 C3 线性化算法能正确执行

应用示例

动态替换基类

场景:将非类对象(如函数或实例)作为基类,动态替换为有效类。

class RealBase:
    def method(self):
        print("RealBase method")


class FakeBase:
    def __mro_entries__(self, bases):
        print("调用 __mro_entries__,替换基类")
        return (RealBase,)  # 返回实际基类元组


class SubClass(FakeBase()):  # FakeBase 实例作为基类
    pass


obj = SubClass()
obj.method()  # 输出: RealBase method
print(SubClass.__bases__)  # 输出: (<class '__main__.RealBase'>,)

说明

  • FakeBase 实例通过 __mro_entries__ 返回 RealBase 类,使得 SubClass 的实际基类变为 RealBase
  • __mro_entries__bases 参数接收原始基类列表(本例为 (FakeBase(),))。

注意事项

  1. 返回值要求:
    • 必须返回一个非空元组,否则抛出 TypeError
    • 元组中的元素必须是类对象(type 实例)。
  2. 执行优先级:
    • __mro_entries__在类创建的基类解析阶段执行,早于元类(__prepare____new__)和__init_subclass__方法。
  3. 设计限制:
    • 无法通过该方法修改类的直接父类数量(仅替换内容)。
    • 若多个基类触发 __mro_entries__,则按声明顺序依次处理。

object.__prepare__(cls, name, bases)

__prepare__是Python元类(metaclass)中的特殊方法,用于在类定义阶段控制类命名空间的创建。它允许开发者自定义类属性的存储方式(如使用有序字典),常用于记录属性定义顺序、动态注入元数据或实现高级类行为控制。当一个类被创建时,__prepare__会在类定义体被执行之前调用,返回一个字典或映射对象,作为类的命名空间(namespace),用于存储类定义中的属性和方法。以下是其核心用法及典型应用场景:

功能特性

  1. 触发时机:

    • __prepare__是元类方法中最先执行的,在类代码块(class语句体)执行前被调用,用于创建类命名空间。
    • 返回值必须是一个映射对象(如字典),用于收集类定义中的所有属性和方法。
  2. 方法签名(需要被定义为类方法):

    @classmethod
    def __prepare__(cls, name, bases) -> Mapping
    
    • cls:元类自身(如type的子类)。
    • name:待创建类的名称(字符串)。
    • bases:基类元组。
  3. 执行流程:

    __prepare__ → 执行类代码块(收集属性到返回的命名空间) → __new__ → __init__
    

应用示例

元类属性管理
class MetaClass(type):
    @classmethod
    def __prepare__(cls, name, bases):
        print('__prepare__', name, bases)
        # 子类会拥有version属性
        namespace = {
            'version': '1.0.0'
        }
        # 需返回一个可映射对象
        return namespace

    def __new__(cls, name, bases, attrs):
        print('__new__', name, bases, attrs)
        return super().__new__(cls, name, bases, attrs)

    def __init__(cls, name, bases, attrs):
        print('__init__', name, bases, attrs)
        return super().__init__(name, bases, attrs)


class CustomClass(metaclass=MetaClass):
    name = 'custom'

    def method(self):
        print('method')


# __prepare__ CustomClass ()
# __new__ CustomClass () {'version': '1.0.0', '__module__': '__main__', '__qualname__': 'CustomClass', 'name': 'custom', 'method': <function CustomClass.method at 0x00000175BB9C3880>}
# __init__ CustomClass () {'version': '1.0.0', '__module__': '__main__', '__qualname__': 'CustomClass', 'name': 'custom', 'method': <function CustomClass.method at 0x00000175BB9C3880>}

上述代码示例主要搞清楚__prepare__的执行顺序和作用。CustomClass继承MetaClass,命名空间中有version属性,可以达到子类都具有version属性。

类属性校验
class UppercaseMeta(type):
    @classmethod
    def __prepare__(cls, name, bases):
        # 验证属性必须大写的类
        class ValidatingDict(dict):
            def __setitem__(self, key, value):
                print("setitem", key, value)
                if not key.isupper() and not key.startswith("__"):
                    raise ValueError(f"属性名 {key} 必须全大写")
                super().__setitem__(key, value)
        return ValidatingDict()


class Settings(metaclass=UppercaseMeta):
    DEBUG = True   # 正确
    # port = 8080    # 抛出 ValueError

说明:通过拦截__setitem__实现命名规范校验

注意事项

  1. 返回值类型:
    • 必须返回映射对象(实现__setitem____getitem__),常用dict或其子类。
  2. __new__/__init__的协作:
    • __prepare__返回的命名空间会传递给元类的__new____init__方法。
  3. 性能影响:
    • 高频创建类时避免复杂逻辑,优先使用Python 3.6+的默认有序字典。

__class__

__class__ 是 Python 中所有对象的内置属性,用于指向实例所属的类。它提供了动态访问类信息的能力,是元编程和反射机制的核心工具之一。以下是其核心用法及典型案例:

功能特性

  1. 获取对象所属类:
    • 实例对象调用 obj.__class__ 返回其所属类对象。
    • 类调用 Class.__class__ 返回其元类(默认为 type)。
  2. 动态类型操作:
    • 修改实例的 __class__ 属性可动态改变其类型(需谨慎使用)。
    • 获取类名、方法列表等元信息。
  3. 应用场景:
    • 类型检查、日志记录、动态类转换、反射编程。

应用示例

获取类名与类型信息

通过 __class__.__name__ 获取类名,常用于调试和日志:

class Animal:
    def get_class_name(self):
        return self.__class__.__name__


dog = Animal()
print(dog.get_class_name())  # 输出: Animal
print(dog.__class__)         # 输出: <class '__main__.Animal'>
print(Animal.__class__)      # 输出: <class 'type'>(类本身的元类)

说明

  • self.__class__.__name__直接获取类名。
  • 类对象的 __class__ 指向元类(如 type)。
类型转换

修改 __class__ 可改变实例类型(需保证类结构兼容)

class Cat:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "Meow"


class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        return "Woof"


animal = Cat("meme")
print(animal.__class__, animal.speak(), isinstance(animal, Cat))        # <class '__main__.Cat'> Meow True

animal.__class__ = Dog       # 注意修改类, 但是并不会重新实例化,因此直接修改类存在风险
print(animal.__class__, animal.speak(), isinstance(animal, Dog))        # <class '__main__.Dog'> Woof True

小结

魔术方法 功能 应用
object.__init__subclass__(cls) 在子类被定义时触发,用于动态修改子类行为 自动注册子类(如插件系统)、为子类注入属性或方法、校验子类规范
object.__set_name__(self, owner, name) 在描述符被声明为类属性时自动捕获属性名 数据校验(如类型/范围检查)、ORM 字段映射、动态生成属性名(如私有变量)
object.__mro_entries__(self, bases) 动态替换非类对象作为基类,返回实际参与MRO的基类元组 泛型编程(如 list[int] 转换为 list)、动态继承链适配
object.__prepare__(cls, name, bases) 在类定义前创建自定义命名空间(如有序字典) 元编程(记录属性定义顺序)、框架开发(如 Django ORM 字段跟踪)
__class__ 内置属性,指向实例所属的类 动态类型转换(如修改实例类型)、反射编程(获取类信息)、类型检查

自定义实例和子类检查

type.__instancecheck__(self, instance)

__instancecheck__ 是 Python 中用于自定义 isinstance() 函数行为的核心魔法方法,需定义在元类中。它允许开发者覆盖默认的实例检查逻辑,常用于实现鸭子类型(Duck Typing)或动态类型匹配。以下是其核心用法及典型应用场景:

功能特性

  1. 方法定位:
    • 需在元类中定义 __instancecheck__(cls, instance),其中 cls 是元类实例化的类,instance 是被检测的对象。
    • 当调用 isinstance(instance, MyClass) 时,Python 会调用 type(MyClass)(即元类)的 __instancecheck__ 方法。
  2. isinstance 的关系:
    • 默认的isinstance检查基于继承关系,而__instancecheck__可绕过继承链,直接按自定义逻辑判断实例类型。
  3. 优先级规则:
    • __instancecheck__优先级高于默认的类型检查,若元类实现此方法,则完全覆盖默认逻辑。

应用示例

实现鸭子类型检测

场景:将拥有特定属性或方法的对象视为某个类的实例。

class DuckTypeMeta(type):
    def __instancecheck__(cls, instance):
        print("__instancecheck__ run")
        # 若对象有 "quack" 和 "fly" 方法,则视为 Duck 类的实例
        return hasattr(instance, "quack") and hasattr(instance, "fly")


class Duck(metaclass=DuckTypeMeta):
    pass


class Bird:
    def quack(self):
        print("Quack!")

    def fly(self):
        print("Flying!")


class Dog:
    def quack(self):
        print("Woof!")


# 测试
bird = Bird()
dog = Dog()

print(isinstance(bird, Duck))   # 输出: True(符合鸭子类型)
print(isinstance(dog, Duck))    # 输出: False

说明:即使 Bird 未继承 Duck,只要满足方法条件即可通过 isinstance 检查。

注意事项

  1. 递归调用风险:

    • __instancecheck__内部再次调用isinstancetype(),可能导致无限递归。

    • 错误示例:

      class BadMeta(type):  
          def __instancecheck__(cls, instance):  
              return isinstance(instance, cls)   # 无限递归
      
  2. 元类必须正确继承:

    • 元类需继承自type,否则无法触发__instancecheck__逻辑。
  3. 与抽象基类的结合:

    • 结合 ABCMeta 元类可实现接口协议的动态检查(如 collections.abc.Iterable

type.__subclasscheck__(self, subclass)

__subclasscheck__ 是 Python 中用于自定义 issubclass() 函数行为的核心魔法方法,需定义在元类中。它允许开发者覆盖默认的子类继承检查逻辑,常用于动态类型匹配、接口协议验证等场景。以下是其核心用法及典型案例:

功能特性

  1. 方法定位:
    • 需在元类中定义 __subclasscheck__(cls, subclass),其中 cls 是元类实例化的类,subclass 是被检测的类。
    • 当调用 issubclass(SubClass, MyClass) 时,Python 会调用 type(MyClass)(即元类)的 __subclasscheck__ 方法。
    • 默认的issubclass检查基于继承关系,而__subclasscheck__可绕过继承链,直接按自定义逻辑判断类关系。
  2. 返回值规则:
    • 必须返回布尔值TrueFalse,若返回NotImplemented则回退到默认继承检查。

应用示例

鸭子类型接口校验

场景:将具有特定方法的类视为子类,无需显式继承。

class ProtocolMeta(type):
    def __subclasscheck__(cls, subclass):
        # 检测类是否实现 "load" 和 "save" 方法
        print("run subclasscheck")
        required_methods = ["load", "save"]
        return all(hasattr(subclass, method) for method in required_methods)


class DataProtocol(metaclass=ProtocolMeta):
    pass


class CSVHandler:
    def load(self):
        ...

    def save(self):
        ...


class JSONHandler:
    def load(self):
        ...


print(issubclass(CSVHandler, DataProtocol))    # 输出: True
print(issubclass(JSONHandler, DataProtocol))   # 输出: False

注意事项

  1. 递归调用风险:
    • 避免在__subclasscheck__内部再次调用issubclass(),否则可能导致无限递归。
  2. 元类必须正确继承:
    • 元类需继承自type,否则无法触发方法逻辑。
  3. 与抽象基类的协作:
    • 结合abc.ABCMeta元类可实现更复杂的接口协议检查(如 collections.abc.Iterable)。

小结

魔术方法 功能 应用
type.__instancecheck__(self, instance) 自定义 isinstance 逻辑(需定义在元类中) 鸭子类型校验(检查方法存在性)、泛型容器校验(如元素类型一致性)
type.__subclasscheck__(self, subclass) 自定义 issubclass 逻辑(需定义在元类中) 协议接口校验(如强制子类实现方法)、动态类型系统扩展(如抽象基类)

模拟泛型类型

object.__class_getitem__(cls, key)

__class_getitem__ 是 Python 3.5 引入的特殊方法,用于实现泛型类型的参数化,支持通过方括号语法(如 List[int])动态生成泛型别名(GenericAlias)。它在类型提示(Type Hints)和静态类型检查中扮演核心角色,允许开发者定义与特定类型参数绑定的泛型类。以下是其核心功能、应用场景及代码示例:

功能特性

  1. 泛型参数化:通过类名[类型参数]的语法动态生成泛型别名,例如List[int]表示元素类型为整数的列表。
  2. 类型注解支持:使自定义类能够与 Python 的类型提示系统兼容,提升代码的可读性和静态检查能力。
  3. 当在类上定义时,会自动成为类方法,不需要使用@classmethod装饰。
  4. 返回 GenericAlias 对象:调用__class_getitem__会返回一个types.GenericAlias类型的对象,表示参数化后的泛型类型。

应用示例

场景 作用 优势
泛型容器定义 创建支持类型参数的容器类(如 MyList[int] 增强代码表达能力和类型安全
协议接口扩展 定义方法的泛型返回值(如 re.Match[str] 统一不同数据类型的接口处理
类型系统扩展 实现类似函数式编程中的泛型概念(如 Functor) 提升代码抽象层级
基础泛型类定义

定义一个泛型容器,支持类型参数化:

import types
from typing import Any, TypeVar


class MyGenericStr:
    def __init__(self, value):
        self.value = value

    def __class_getitem__(cls, item) -> types.GenericAlias:
        print("__class_getitem__", item)
        # 返回标准化的 GenericAlias 对象
        return types.GenericAlias(cls, item)

    def __getitem__(self, key):
        print("__getitem__", key)
        return self.value[key]


# 参数化泛型
a = MyGenericStr[str]  # __class_getitem__ <class 'str'>
print(type(a))         # <class 'types.GenericAlias'>

b = MyGenericStr("abcde")
print(b[2])            # __getitem__ 2
                       # c

说明

  • MyGenericList[int] 会调用 __class_getitem__item 参数接收 int 类型。
  • 主要用于类型标注,实际逻辑需结合 __getitem__ 等方法实现容器操作

注意__class_getitem____getitem__的区别:

使用方括号语法获取一个对象时会调用在该对象的类上定义的__getitem__实例方法。如果抽取一个类时则会调用__class_getitem__方法,如上述代码示例所示。

魔术方法 功能 应用
classmethod object.__class_getitem__(cls, key) 支持泛型类型的参数化语法(如 ClassName[Type]),返回参数化类型的 GenericAlias 对象 类型提示中定义泛型参数(如 List[int]),与静态类型检查工具兼容的类型系统扩展。

模拟可调用对象

object.__call__(self[, args...])

__call__ 是 Python 中让类的实例像函数一样可被调用的魔术方法。它允许对象通过 object() 语法触发自定义逻辑,常用于实现装饰器、延迟计算、状态保留的可调用对象等场景。以下是其核心用法及典型案例:

功能特性

  1. 使实例可调用: 定义 __call__ 方法后,类的实例可以直接像函数一样调用(例如 obj())。
  2. 参数传递: 支持传入任意参数(*args**kwargs)。
  3. 状态保留: 可在多次调用间保持实例内部状态(如计数器、缓存数据)。

应用示例

应用场景 作用 优势
装饰器类 为函数添加可配置的额外逻辑(如重试、日志) 参数化管理,状态保持
状态追踪器 记录调用次数、参数或结果 调试与测试友好
延迟计算 按需执行耗时操作(如文件加载、网络请求) 优化性能,减少资源浪费
让对象可调用
class Adder:
    def __init__(self, base):
        self.base = base  # 初始化时保存基准值

    def __call__(self, x):
        return self.base + x

add5 = Adder(5)  # 创建一个基准为5的加法器
print(add5(3))   # 输出: 8(等效于调用 add5.__call__(3))

应用场景:

  • 创建有预设参数的运算器(如税率计算器、单位转换器)。
带参数的装饰器类

通过 __call__ 实现类装饰器,支持参数传递和状态管理:

class RetryDecorator:
    def __init__(self, max_retries=3):
        self.max_retries = max_retries  # 装饰器参数(最大重试次数)

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            for _ in range(self.max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"重试中,错误: {e}")
            raise RuntimeError("超过最大重试次数")
        return wrapper

@RetryDecorator(max_retries=2)
def risky_operation():
    import random
    if random.random() < 0.8:
        raise ValueError("随机失败")
    return "成功"

print(risky_operation())  # 输出: 两次重试后可能成功或抛错

优势:

  • 装饰器类可保存配置参数(如重试次数),复用性强。
状态追踪器(保留调用记录)

记录实例被调用的次数和参数:

class CallTracker:
    def __init__(self):
        self.call_count = 0
        self.call_args = []

    def __call__(self, *args, **kwargs):
        self.call_count += 1
        self.call_args.append((args, kwargs))
        print(f"第 {self.call_count} 次调用,参数: {args}, {kwargs}")

tracker = CallTracker()
tracker(1, 2, a=3)  # 输出: 第 1 次调用,参数: (1, 2), {'a': 3}
tracker(4, b=5)     # 输出: 第 2 次调用,参数: (4,), {'b': 5}
延迟计算(惰性求值)

在首次调用时执行复杂计算,并缓存结果:

class LazyDataLoader:
    def __init__(self, data_source):
        self.data_source = data_source
        self._data = None  # 数据未加载

    def __call__(self):
        if self._data is None:
            print("首次加载数据...")
            self._data = self._load_data()
        return self._data

    def _load_data(self):
        # 模拟耗时加载
        return f"从 {self.data_source} 加载的数据"

loader = LazyDataLoader("数据库")
print(loader())  # 输出: 首次加载数据... → 从 数据库 加载的数据
print(loader())  # 直接返回缓存数据

优势:

  • 优化性能,避免重复计算或资源加载。

注意事项

  1. 避免无限递归:
    • 若在 __call__ 中不小心再次调用自身(如 self()),会导致栈溢出。
  2. 与普通函数的区别:
    • 可调用对象本质是实例,可以持有状态,普通函数无法直接保存数据。
  3. 性能影响:
    • 高频调用时,实例方法调用的开销略高于普通函数,需权衡使用场景。

object.__len__(self)

功能特性

__len__ 是 Python 中的一个特殊方法(魔术方法),用于定义对象的"长度"或"大小"。

核心特性:

  • 当调用内置函数 len(obj) 时,Python 会自动调用该对象的 __len__ 方法
  • 必须返回一个非负整数(>= 0)
  • 如果对象未定义 __len__ 方法,调用 len(obj) 会抛出 TypeError

触发机制:

my_list = [1, 2, 3]
length = len(my_list)  # 实际上调用的是 my_list.__len__()

应用示例

自定义容器类

实现类似列表、集合的自定义容器时,需要定义 __len__ 来支持长度查询。

class Playlist:
    def __init__(self):
        self.songs = []
    
    def add_song(self, song):
        self.songs.append(song)
    
    def __len__(self):
        return len(self.songs)

# 使用示例
my_playlist = Playlist()
my_playlist.add_song("Song A")
my_playlist.add_song("Song B")
print(len(my_playlist))  # 输出: 2
# 解释:len(my_playlist) 调用了 Playlist.__len__(),返回歌曲数量
布尔上下文中的隐式调用

在布尔判断中,如果对象没有 __bool__ 方法,Python 会使用 __len__ 的结果。

class ShoppingCart:
    def __init__(self):
        self.items = []
    
    def add_item(self, item):
        self.items.append(item)
    
    def __len__(self):
        return len(self.items)

# 使用示例
cart = ShoppingCart()
if cart:  # 相当于 if len(cart) != 0
    print("购物车不为空")
else:
    print("购物车为空")  # 输出: 购物车为空

cart.add_item("商品A")
if cart:
    print("购物车不为空")  # 输出: 购物车不为空

注意事项

返回值要求
class InvalidLengthExample:
    def __len__(self):
        return -1  # 错误:不能返回负数

obj = InvalidLengthExample()
# len(obj)  # 这会抛出 ValueError
性能考虑

对于大型数据集,__len__ 应该具有 O(1) 时间复杂度,避免复杂的计算。

class EfficientCollection:
    def __init__(self):
        self._data = []
        self._length = 0  # 缓存长度以提高性能
    
    def add_item(self, item):
        self._data.append(item)
        self._length += 1
    
    def __len__(self):
        return self._length  # O(1) 时间复杂度
__bool__ 的关系

如果同时定义了 __len____bool__,在布尔上下文中 __bool__ 优先级更高。

class CustomContainer:
    def __init__(self, data):
        self.data = data
    
    def __len__(self):
        return len(self.data)
    
    def __bool__(self):
        return any(self.data)  # 检查是否有真值元素

container = CustomContainer([0, 0, 0])
print(len(container))  # 输出: 3
print(bool(container))  # 输出: False (因为所有元素都是0)
内置类型的 __len__ 行为

了解常见内置类型的长度定义:

  • 列表/元组:元素数量
  • 字符串:字符数量
  • 字典:键值对数量
  • 集合:元素数量

__len__ 方法是使自定义对象与 Python 生态系统无缝集成的重要工具之一。

object.__length_hint__(self)

功能特性

__length_hint__ 是 Python 3.4 中引入的一个特殊方法,它为对象提供一个估计的长度或大小值,主要用于优化 len() 函数的使用. 这种估计方法并不返回对象的精确长度,而是提供一个快速的、不精确的猜测,以帮助解释器在需要时进行优化。

目的:此方法旨在为一些容器提供一个“预估”长度,这样当 len() 函数被调用时,解释器就不必进行昂贵的操作(例如,迭代整个列表)来获取准确的长度。

何时使用:它通常用于那些可以方便地估计大小,但计算精确大小会很慢或很耗费资源的对象.

实现:当一个对象的 __length_hint__() 方法存在时,len() 函数会首先调用这个方法来获取长度信息.

主要被 operator.length_hint() 函数调用

import operator
hint = operator.length_hint(obj)  # 调用 obj.__length_hint__()

应用示例

自定义迭代器

为需要预分配内存的自定义迭代器提供长度提示。

import operator

class BatchedData:
    def __init__(self, total_size, batch_size):
        self.total_size = total_size
        self.batch_size = batch_size
        self.processed = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.processed >= self.total_size:
            raise StopIteration
        
        batch_end = min(self.processed + self.batch_size, self.total_size)
        batch = list(range(self.processed, batch_end))
        self.processed = batch_end
        return batch
    
    def __length_hint__(self):
        # 返回剩余批次数的估计
        remaining = self.total_size - self.processed
        return (remaining + self.batch_size - 1) // self.batch_size  # 向上取整

# 使用示例
batcher = BatchedData(total_size=100, batch_size=10)
print(f"预计剩余批次: {operator.length_hint(batcher)}")  # 输出: 10

# 处理一些批次
next(batcher)  # 处理第一个批次
next(batcher)  # 处理第二个批次

print(f"处理后的剩余批次估计: {operator.length_hint(batcher)}")  # 输出: 8
流式数据处理

为无法预先知道确切长度的数据流提供估计。

import operator
import random

class StreamingReader:
    def __init__(self, estimated_total):
        self.estimated_total = estimated_total
        self.items_yielded = 0
        # 模拟流数据,实际长度可能比估计值多或少
        self.actual_length = estimated_total + random.randint(-5, 5)
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.items_yielded >= self.actual_length:
            raise StopIteration
        
        # 模拟读取数据
        data = f"item_{self.items_yielded}"
        self.items_yielded += 1
        return data
    
    def __length_hint__(self):
        # 返回剩余项目的估计
        return max(0, self.estimated_total - self.items_yielded)

# 使用示例
stream = StreamingReader(estimated_total=20)
print(f"初始长度估计: {operator.length_hint(stream)}")  # 输出: 20

# 模拟处理过程
for i, item in enumerate(stream):
    if i == 5:
        print(f"处理5项后的估计: {operator.length_hint(stream)}")  # 输出: 15
        break

注意事项

__len__ 的区别
class Collection:
    def __len__(self):
        # 必须返回精确值
        return self.exact_count()
    
    def __length_hint__(self):
        # 可以返回估计值,用于优化
        return self.estimate_count()
返回值要求

__length_hint__ 应该返回非负整数,但如果无法提供合理估计,可以返回 0。

class UnknownLengthIterator:
    def __iter__(self):
        return self
    
    def __next__(self):
        # 从无限流或未知源读取
        pass
    
    def __length_hint__(self):
        return 0  # 表示无法估计
性能优化用途

主要用于内存预分配等优化场景:

# 内部可能的使用方式
hint = operator.length_hint(iterator)
if hint:
    # 根据提示预分配内存
    result = [None] * hint
else:
    # 没有提示,动态扩展
    result = []
内置类型的使用

一些内置迭代器类型实现了 __length_hint__

# 列表迭代器有长度提示
lst = [1, 2, 3, 4, 5]
it = iter(lst)
print(operator.length_hint(it))  # 输出: 5

# 消费一个元素后
next(it)
print(operator.length_hint(it))  # 输出: 4

__length_hint__ 是一个主要用于性能优化的高级特性,在需要为迭代操作提供内存预分配提示时特别有用。

object.__getitem__(self, key)

功能特性

__getitem__ 允许自定义类的对象支持 [] 索引操作,从而能够像列表或字典一样获取元素。当在对象上执行 obj[key] 这样的操作时,Python 会自动调用 __getitem__ 方法来处理。该方法接收一个键 key(可以是整数索引或其他类型的键),并返回与该键对应的值。

主要功能和用法

  • 启用索引操作:通过实现 __getitem__,可以让自定义对象支持通过中括号 [] 进行元素的访问,使代码更直观。
  • 支持不同类型的键:__getitem__ 不仅支持整数索引(类似列表),还可以根据需要处理其他类型的键,例如字符串(类似字典)。
  • 处理切片:__getitem__ 还可以处理切片操作,如 obj[start:stop:step]
  • 自定义访问逻辑:你可以在 __getitem__ 方法中编写自己的逻辑,例如在获取元素时进行数据验证或执行其他操作

应用示例

自定义序列类

创建支持索引访问的自定义序列类型。

class CustomSequence:
    def __init__(self, data):
        self.data = list(data)
    
    def __getitem__(self, index):
        if isinstance(index, slice):
            # 处理切片操作
            return CustomSequence(self.data[index])
        elif isinstance(index, int):
            # 处理整数索引
            return self.data[index]
        else:
            raise TypeError("索引必须是整数或切片")
    
    def __len__(self):
        return len(self.data)
    
    def __repr__(self):
        return f"CustomSequence({self.data})"

# 使用示例
seq = CustomSequence([10, 20, 30, 40, 50])
print(seq[1])      # 输出: 20 (整数索引)
print(seq[1:4])    # 输出: CustomSequence([20, 30, 40]) (切片操作)
print(seq[::2])    # 输出: CustomSequence([10, 30, 50]) (步长切片)
字典风格的键值访问

实现类似字典的键值对访问。

class ConfigDictionary:
    def __init__(self):
        self._settings = {
            'theme': 'dark',
            'language': 'python',
            'auto_save': True,
            'font_size': 12
        }
    
    def __getitem__(self, key):
        if key in self._settings:
            return self._settings[key]
        else:
            raise KeyError(f"配置项 '{key}' 不存在")
    
    def __setitem__(self, key, value):
        self._settings[key] = value
    
    def keys(self):
        return self._settings.keys()

# 使用示例
config = ConfigDictionary()
print(config['theme'])        # 输出: dark
print(config['language'])     # 输出: python
# print(config['invalid_key']) # 会抛出 KeyError
多维数据结构

实现多维数组或矩阵的索引访问。

class Matrix:
    def __init__(self, rows, cols, fill_value=0):
        self.rows = rows
        self.cols = cols
        self.data = [[fill_value] * cols for _ in range(rows)]
    
    def __getitem__(self, index):
        if isinstance(index, tuple) and len(index) == 2:
            # 处理 matrix[row, col] 语法
            row, col = index
            if 0 <= row < self.rows and 0 <= col < self.cols:
                return self.data[row][col]
            else:
                raise IndexError("矩阵索引超出范围")
        elif isinstance(index, int):
            # 处理 matrix[row] 返回整行
            if 0 <= index < self.rows:
                return self.data[index]
            else:
                raise IndexError("行索引超出范围")
        else:
            raise TypeError("无效的索引类型")

# 使用示例
matrix = Matrix(3, 3)
matrix.data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

print(matrix[1, 2])    # 输出: 6 (第1行第2列)
print(matrix[0])       # 输出: [1, 2, 3] (第0行)

注意事项

切片对象处理

正确处理切片对象是 __getitem__ 实现中的重要部分。

class SliceAware:
    def __getitem__(self, index):
        if isinstance(index, slice):
            start = index.start if index.start is not None else 0
            stop = index.stop if index.stop is not None else float('inf')
            step = index.step if index.step is not None else 1
            print(f"切片: start={start}, stop={stop}, step={step}")
            # 返回切片结果...
        else:
            print(f"索引: {index}")
            # 返回单个元素...

obj = SliceAware()
obj[5]          # 输出: 索引: 5
obj[1:10:2]     # 输出: 切片: start=1, stop=10, step=2
obj[:5]         # 输出: 切片: start=0, stop=5, step=1
错误处理约定

遵循 Python 的错误处理约定:如果索引无效或不被支持,应该抛出相应的异常,如 IndexErrorTypeError

class ProperErrorHandling:
    def __init__(self, data):
        self.data = data
    
    def __getitem__(self, key):
        try:
            return self.data[key]
        except (IndexError, KeyError) as e:
            # 重新抛出适当的异常类型
            if isinstance(key, int):
                raise IndexError("索引超出范围") from e
            else:
                raise KeyError(f"键 '{key}' 不存在") from e

collection = ProperErrorHandling([1, 2, 3])
# collection[10]  # 抛出 IndexError
# collection['x'] # 抛出 KeyError
与迭代的关系

如果类定义了 __getitem__ 但没有定义 __iter__,Python 会尝试使用索引从 0 开始迭代。

class ImplicitIterable:
    def __init__(self, data):
        self.data = data
    
    def __getitem__(self, index):
        if index >= len(self.data):
            raise IndexError
        return self.data[index]
    
    def __len__(self):
        return len(self.data)

obj = ImplicitIterable(['a', 'b', 'c'])
for item in obj:  # 即使没有 __iter__,也能迭代
    print(item)   # 输出: a, b, c

object.__setitem__(self, key, value)

功能特性

__setitem__ 允许为类实例的元素设置值,当使用方括号 [] 操作符进行赋值时(例如 instance[key] = value)会被调用。这个方法是字典和列表等容器类型的核心,不返回任何值(应该返回 None),通过实现它,可以在自定义类中模拟类似的行为,实现对实例中特定“键”或“索引”所对应的值进行设置。

  • self: 指向调用该方法的实例本身,通过 self 可以在方法内部访问和修改实例的属性和状态。
  • key: 要设置的元素的键或索引。例如,在列表中可能是索引 0,在字典中可能是键 'name'
  • value: 要赋给该键或索引的值。
  • 调用方式: 无需直接调用 __setitem__ 方法,而是通过 instance[key] = value 的语法来调用它。

应用示例

自定义可变序列

创建支持索引赋值操作的自定义序列类型。

class CustomList:
    def __init__(self, initial_data=None):
        self._data = list(initial_data) if initial_data else []
    
    def __getitem__(self, index):
        return self._data[index]
    
    def __setitem__(self, index, value):
        if isinstance(index, slice):
            # 处理切片赋值
            if isinstance(value, (list, tuple)):
                self._data[index] = value
            else:
                raise TypeError("切片赋值需要可迭代对象")
        elif isinstance(index, int):
            # 处理整数索引赋值
            if index < 0:
                index = len(self._data) + index
            if 0 <= index < len(self._data):
                self._data[index] = value
            else:
                raise IndexError("索引超出范围")
        else:
            raise TypeError("索引必须是整数或切片")
    
    def __len__(self):
        return len(self._data)
    
    def __repr__(self):
        return f"CustomList({self._data})"

# 使用示例
clist = CustomList([1, 2, 3, 4, 5])
clist[1] = 20           # 设置单个元素
print(clist)            # 输出: CustomList([1, 20, 3, 4, 5])

clist[1:4] = [22, 33, 44]  # 切片赋值
print(clist)            # 输出: CustomList([1, 22, 33, 44, 5])

clist[-1] = 99          # 负索引赋值
print(clist)            # 输出: CustomList([1, 22, 33, 44, 99])
字典风格的键值设置

实现类似字典的键值对设置功能。

class ConfigManager:
    def __init__(self, initial_config=None):
        self._config = dict(initial_config) if initial_config else {}
        self._validation_rules = {
            'port': lambda x: isinstance(x, int) and 0 < x < 65536,
            'timeout': lambda x: isinstance(x, (int, float)) and x > 0,
            'debug': lambda x: isinstance(x, bool)
        }
    
    def __getitem__(self, key):
        return self._config[key]
    
    def __setitem__(self, key, value):
        # 验证配置值
        if key in self._validation_rules:
            if not self._validation_rules[key](value):
                raise ValueError(f"配置项 '{key}' 的值 '{value}' 无效")
        
        self._config[key] = value
    
    def __contains__(self, key):
        return key in self._config
    
    def items(self):
        return self._config.items()

# 使用示例
config = ConfigManager()
config['port'] = 8080           # 有效赋值
config['debug'] = True          # 有效赋值
# config['port'] = -1           # 会抛出 ValueError
# config['timeout'] = 'invalid' # 会抛出 ValueError

print(dict(config.items()))     # 输出: {'port': 8080, 'debug': True}
多维数据结构的赋值

实现矩阵或多维数组的索引赋值。

class Matrix2D:
    def __init__(self, rows, cols, fill_value=0):
        self.rows = rows
        self.cols = cols
        self._data = [[fill_value] * cols for _ in range(rows)]
    
    def __getitem__(self, index):
        if isinstance(index, tuple) and len(index) == 2:
            row, col = index
            return self._data[row][col]
        elif isinstance(index, int):
            return self._data[index]
        else:
            raise TypeError("索引必须是整数或(row, col)元组")
    
    def __setitem__(self, index, value):
        if isinstance(index, tuple) and len(index) == 2:
            row, col = index
            if 0 <= row < self.rows and 0 <= col < self.cols:
                self._data[row][col] = value
            else:
                raise IndexError("矩阵索引超出范围")
        elif isinstance(index, int):
            if 0 <= index < self.rows:
                if isinstance(value, (list, tuple)) and len(value) == self.cols:
                    self._data[index] = list(value)
                else:
                    raise ValueError(f"行赋值需要长度为{self.cols}的序列")
            else:
                raise IndexError("行索引超出范围")
        else:
            raise TypeError("索引必须是整数或(row, col)元组")
    
    def __str__(self):
        return '\n'.join(' '.join(map(str, row)) for row in self._data)

# 使用示例
matrix = Matrix2D(3, 3)
matrix[0, 0] = 1       # 设置单个元素
matrix[1, 1] = 5       # 设置单个元素
matrix[2] = [7, 8, 9]  # 设置整行

print("矩阵内容:")
print(matrix)
# 输出:
# 1 0 0
# 0 5 0
# 7 8 9

注意事项

切片赋值的处理

正确处理切片赋值需要考虑各种边界情况。

class SliceAssignment:
    def __init__(self, data):
        self.data = list(data)
    
    def __setitem__(self, index, value):
        if isinstance(index, slice):
            # 扩展切片处理逻辑
            if not hasattr(value, '__iter__'):
                raise TypeError("切片赋值需要可迭代对象")
            
            # 将切片转换为具体范围
            start, stop, step = index.indices(len(self.data))
            
            if step == 1:
                # 普通切片,直接替换
                self.data[start:stop] = value
            else:
                # 带步长的切片,需要长度匹配
                value_list = list(value)
                slice_len = len(range(start, stop, step))
                if len(value_list) != slice_len:
                    raise ValueError(
                        f"尝试将长度为{len(value_list)}的序列"
                        f"赋给长度为{slice_len}的切片"
                    )
                
                # 逐个赋值
                for i, val in zip(range(start, stop, step), value_list):
                    self.data[i] = val
        else:
            # 普通索引赋值
            self.data[index] = value
__getitem__ 的一致性

确保 __setitem____getitem__ 的行为一致。

class ConsistentContainer:
    def __init__(self):
        self._items = {}
    
    def __getitem__(self, key):
        # 对键进行标准化处理
        normalized_key = self._normalize_key(key)
        return self._items[normalized_key]
    
    def __setitem__(self, key, value):
        # 使用相同的标准化逻辑
        normalized_key = self._normalize_key(key)
        self._items[normalized_key] = value
    
    def _normalize_key(self, key):
        # 确保键处理逻辑一致
        if isinstance(key, str):
            return key.lower().strip()
        return key
异常处理

提供清晰的错误信息,遵循 Python 的异常约定。

class RobustContainer:
    def __init__(self, max_size=1000):
        self.max_size = max_size
        self._data = []
    
    def __setitem__(self, index, value):
        try:
            # 处理负索引
            if index < 0:
                index = len(self._data) + index
            
            # 边界检查
            if index < 0 or index >= len(self._data):
                raise IndexError(f"索引 {index} 超出范围 [0, {len(self._data)-1}]")
            
            # 大小限制检查
            if len(self._data) >= self.max_size:
                raise MemoryError(f"容器已达到最大大小 {self.max_size}")
            
            self._data[index] = value
            
        except TypeError as e:
            raise TypeError(f"无效的索引类型: {type(index)}") from e

object.__delitem__(self, key)

功能特性

__delitem__ 用于实现对象的索引或键的删除操作。它被方括号的删除操作符del obj[key]调用。

通常与 __getitem____setitem__ 配合使用,实现完整的容器行为,不返回任何值(应该返回 None)。主要用于删除容器中的特定元素,而不是销毁整个对象

应用示例

自定义可变序列的删除操作

创建支持元素删除的自定义序列类型。

class CustomList:
    def __init__(self, initial_data=None):
        self._data = list(initial_data) if initial_data else []
    
    def __getitem__(self, index):
        return self._data[index]
    
    def __setitem__(self, index, value):
        self._data[index] = value
    
    def __delitem__(self, index):
        if isinstance(index, slice):
            # 处理切片删除
            del self._data[index]
        elif isinstance(index, int):
            # 处理整数索引删除
            if index < 0:
                index = len(self._data) + index
            if 0 <= index < len(self._data):
                del self._data[index]
            else:
                raise IndexError("索引超出范围")
        else:
            raise TypeError("索引必须是整数或切片")
    
    def __len__(self):
        return len(self._data)
    
    def __repr__(self):
        return f"CustomList({self._data})"

# 使用示例
clist = CustomList([1, 2, 3, 4, 5, 6])
print("原始列表:", clist)  # 输出: CustomList([1, 2, 3, 4, 5, 6])

del clist[2]            # 删除索引为2的元素
print("删除索引2后:", clist)  # 输出: CustomList([1, 2, 4, 5, 6])

del clist[1:3]          # 删除切片元素
print("删除切片[1:3]后:", clist)  # 输出: CustomList([1, 5, 6])

del clist[-1]           # 删除最后一个元素
print("删除最后一个元素后:", clist)  # 输出: CustomList([1, 5])
字典风格的键值删除

实现类似字典的键值对删除功能。

class ConfigDictionary:
    def __init__(self, initial_config=None):
        self._config = dict(initial_config) if initial_config else {}
        self._protected_keys = {'version', 'created_date'}
    
    def __getitem__(self, key):
        return self._config[key]
    
    def __setitem__(self, key, value):
        self._config[key] = value
    
    def __delitem__(self, key):
        # 检查是否为受保护的键
        if key in self._protected_keys:
            raise PermissionError(f"不能删除受保护的配置项: {key}")
        
        if key in self._config:
            del self._config[key]
        else:
            raise KeyError(f"配置项 '{key}' 不存在")
    
    def __contains__(self, key):
        return key in self._config
    
    def items(self):
        return self._config.items()

# 使用示例
config = ConfigDictionary({
    'theme': 'dark',
    'language': 'python',
    'version': '1.0',
    'created_date': '2024-01-01'
})

print("初始配置:", dict(config.items()))
# 输出: {'theme': 'dark', 'language': 'python', 'version': '1.0', 'created_date': '2024-01-01'}

del config['theme']      # 成功删除
print("删除theme后:", dict(config.items()))
# 输出: {'language': 'python', 'version': '1.0', 'created_date': '2024-01-01'}

# del config['version']  # 会抛出 PermissionError
# del config['invalid']  # 会抛出 KeyError

注意事项

切片删除的边界情况

正确处理各种切片删除场景。

class RobustList:
    def __init__(self, data):
        self.data = list(data)
    
    def __delitem__(self, index):
        if isinstance(index, slice):
            start, stop, step = index.indices(len(self.data))
            
            if step == 1:
                # 普通切片删除
                del self.data[start:stop]
            elif step > 0:
                # 正步长切片删除
                indices_to_delete = list(range(start, stop, step))
                # 从后往前删除,避免索引变化
                for i in sorted(indices_to_delete, reverse=True):
                    if i < len(self.data):
                        del self.data[i]
            elif step < 0:
                # 负步长切片删除
                start, stop, step = index.indices(len(self.data))
                indices_to_delete = list(range(start, stop, step))
                for i in sorted(indices_to_delete, reverse=True):
                    if 0 <= i < len(self.data):
                        del self.data[i]
            else:
                raise ValueError("切片步长不能为0")
        else:
            # 普通索引删除
            del self.data[index]

object.__missing__(self, key)

功能特性

__missing__ 用于处理当访问不存在的键时的行为。提供了一种优雅的方式来处理键缺失的情况,而不是直接抛出 KeyError

核心特性:

  • 当通过 __getitem__ 访问不存在的键时自动调用,所以类必须实现 __getitem__ 方法
  • 允许自定义键不存在时的返回值或行为
  • 不会影响 get() 方法、in 操作符或其他字典方法的行为
  • __missing__ 方法需要定义在实现了映射协议(mapping protocol)的类中

触发机制:

value = custom_dict[key]  # 如果key不存在,调用custom_dict.__missing__(key)

应用示例

默认值字典

创建在键不存在时返回默认值的字典。

class DefaultDict(dict):
    def __init__(self, default_factory, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.default_factory = default_factory
    
    def __missing__(self, key):
        """当键不存在时,使用default_factory创建默认值并设置"""
        default_value = self.default_factory()
        self[key] = default_value  # 缓存默认值以供后续使用
        return default_value

# 使用示例
counter = DefaultDict(int)
print(counter['a'])  # 输出: 0 (调用int()得到默认值0)
counter['a'] += 1    # 现在键'a'存在了
print(counter['a'])  # 输出: 1

list_dict = DefaultDict(list)
print(list_dict['items'])  # 输出: [] (调用list()得到空列表)
list_dict['items'].append('apple')
print(list_dict['items'])  # 输出: ['apple']
自动转换键的字典

创建能够自动转换键格式的字典。

class CaseInsensitiveDict(dict):
    def __missing__(self, key):
        """当键不存在时,尝试不同的大小写变体"""
        if isinstance(key, str):
            # 尝试小写版本
            lower_key = key.lower()
            if lower_key in self:
                return self[lower_key]
            
            # 尝试大写版本
            upper_key = key.upper()
            if upper_key in self:
                return self[upper_key]
            
            # 尝试首字母大写版本
            title_key = key.title()
            if title_key in self:
                return self[title_key]
        
        # 如果所有变体都不存在,抛出KeyError
        raise KeyError(f"键 '{key}' 不存在")

# 使用示例
config = CaseInsensitiveDict()
config['ServerHost'] = 'localhost'
config['DATABASE_PORT'] = 5432

print(config['serverhost'])    # 输出: localhost (自动找到小写变体)
print(config['database_port']) # 输出: 5432 (自动找到小写变体)
print(config['Server_Host'])   # 输出: localhost (自动找到正确格式)

# print(config['nonexistent']) # 会抛出 KeyError

注意事项

get() 方法的区别

__missing__ 只影响 __getitem__(即 dict[key]),不影响 get() 方法。

class MissingExample(dict):
    def __missing__(self, key):
        return f"默认值 for {key}"

d = MissingExample()
d['existing'] = '实际值'

print(d['existing'])    # 输出: 实际值
print(d['missing'])     # 输出: 默认值 for missing (触发__missing__)
print(d.get('missing')) # 输出: None (不触发__missing__)
print(d.get('missing', '备用值')) # 输出: 备用值 (不触发__missing__)
避免无限递归

__missing__ 实现中要小心避免无限递归。

class SafeMissingDict(dict):
    def __missing__(self, key):
        # 错误的实现:会导致无限递归
        # return self[key]  # 这会导致无限递归!
        
        # 正确的实现:使用super()或直接返回值
        if key == 'default':
            return '安全默认值'
        else:
            # 使用父类的__getitem__会抛出KeyError,不会递归调用__missing__
            return super().__getitem__('default')

d = SafeMissingDict()
print(d['any_key'])  # 输出: 安全默认值
性能考虑

对于频繁访问的场景,考虑在 __missing__ 中缓存结果。

class CachedMissingDict(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._missing_cache = {}
    
    def __missing__(self, key):
        if key in self._missing_cache:
            return self._missing_cache[key]
        
        # 计算昂贵的默认值
        default_value = self._compute_expensive_default(key)
        self._missing_cache[key] = default_value
        return default_value
    
    def _compute_expensive_default(self, key):
        # 模拟昂贵的计算
        import time
        time.sleep(0.1)  # 模拟耗时操作
        return f"计算出的默认值 for {key}"

d = CachedMissingDict()
# 第一次访问会计算并缓存
print(d['test'])  # 稍微慢一点
# 后续访问相同的缺失键会很快
print(d['test'])  # 很快,使用缓存
继承关系的重要性

__missing__ 必须定义在 dict 的子类中才有效。

# 这个类不会触发__missing__,因为它不是dict的子类
class InvalidMissingClass:
    def __getitem__(self, key):
        if key not in self.__dict__:
            return self.__missing__(key)
        return self.__dict__[key]
    
    def __missing__(self, key):
        return f"缺失键: {key}"

# 正确的做法
class ValidMissingDict(dict):
    def __missing__(self, key):
        return f"缺失键: {key}"

# 测试
invalid = InvalidMissingClass()
# print(invalid['test'])  # 会抛出AttributeError

valid = ValidMissingDict()
print(valid['test'])  # 输出: 缺失键: test

__missing__ 方法为字典子类提供了强大的自定义能力,使得处理缺失键的情况更加灵活和优雅。通过合理实现这个方法,可以创建各种智能字典类型,如默认值字典、自动转换字典、计算字典等,大大提升代码的可读性和可维护性。

object.__iter__(self)

功能特性

__iter__ 方法用于定义一个对象的迭代行为,在对象被迭代时(例如 for 循环中),Python 会调用此方法来返回一个迭代器对象(实现了__next__方法),从而实现对对象的逐个元素遍历。这是一个特殊的“协议方法”,用于实现可迭代对象(iterable)的迭代过程,该方法的核心作用是当需要一个迭代器来遍历一个容器时,它会返回一个全新的迭代器实例。

与迭代器的关系:

  • 可迭代对象 (Iterable): 包含 __iter__() 方法的对象,可以被迭代。
  • 迭代器 (Iterator): 包含 __iter__()__next__() 方法的对象,可以逐步提供序列中的下一个元素。

应用示例

自定义序列迭代器

创建支持迭代的自定义序列类型。

class Countdown:
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        """返回迭代器对象,这里返回self因为类同时实现了__next__"""
        self.current = self.start
        return self
    
    def __next__(self):
        """实现迭代逻辑"""
        if self.current <= 0:
            raise StopIteration
        else:
            value = self.current
            self.current -= 1
            return value

# 使用示例
print("倒计时开始:")
for number in Countdown(5):
    print(number)
# 输出:
# 5
# 4
# 3
# 2
# 1

# 也可以手动使用迭代器
countdown = Countdown(3)
iterator = iter(countdown)
print(next(iterator))  # 输出: 3
print(next(iterator))  # 输出: 2
print(next(iterator))  # 输出: 1
# print(next(iterator))  # 抛出 StopIteration
生成器风格的迭代器

使用生成器函数简化迭代器实现。

class FibonacciSequence:
    def __init__(self, limit=None):
        self.limit = limit
    
    def __iter__(self):
        """使用生成器实现斐波那契数列迭代"""
        a, b = 0, 1
        count = 0
        
        while self.limit is None or count < self.limit:
            yield a
            a, b = b, a + b
            count += 1

# 使用示例
print("前10个斐波那契数:")
for i, fib in enumerate(FibonacciSequence(10)):
    print(f"F({i}) = {fib}")

# 无限序列(需要break)
print("\n小于100的斐波那契数:")
for fib in FibonacciSequence():
    if fib > 100:
        break
    print(fib, end=" ")
print()

# 使用生成器表达式风格
class Squares:
    def __init__(self, n):
        self.n = n
    
    def __iter__(self):
        return (x * x for x in range(self.n))

print("\n平方数:")
for square in Squares(5):
    print(square, end=" ")  # 输出: 0 1 4 9 16

注意事项

迭代器耗尽

迭代器只能迭代一次,再次迭代需要重新创建。

class OneTimeIterable:
    def __init__(self, data):
        self.data = data
    
    def __iter__(self):
        return iter(self.data)  # 返回列表的迭代器

items = OneTimeIterable([1, 2, 3])
iterator = iter(items)

print("第一次迭代:")
for item in iterator:
    print(item)  # 输出: 1, 2, 3

print("第二次迭代:")
for item in iterator:
    print(item)  # 没有输出,迭代器已耗尽

print("重新获取迭代器:")
for item in items:  # 每次调用__iter__返回新的迭代器
    print(item)  # 输出: 1, 2, 3
__getitem__ 的备选方案

如果类没有定义 __iter__ 但定义了 __getitem__,Python 会尝试使用索引从 0 开始迭代。

class FallbackIterable:
    def __init__(self, data):
        self.data = data
    
    def __getitem__(self, index):
        """如果没有__iter__,Python会尝试索引从0开始直到IndexError"""
        if index >= len(self.data):
            raise IndexError
        return self.data[index]
    
    def __len__(self):
        return len(self.data)

# 即使没有__iter__,也能迭代
fallback = FallbackIterable(['a', 'b', 'c'])
for char in fallback:
    print(char)  # 输出: a, b, c
无限迭代器的处理

无限迭代器需要在使用时小心处理,避免无限循环。

class InfiniteCounter:
    def __init__(self, start=0):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        value = self.current
        self.current += 1
        return value

# 使用无限迭代器需要明确的退出条件
counter = InfiniteCounter()
for i, num in enumerate(counter):
    if num > 5:  # 必须要有退出条件
        break
    print(num, end=" ")  # 输出: 0 1 2 3 4 5
状态保持

迭代器需要正确管理内部状态,特别是对于可重置的迭代器。

class ResettableRange:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.reset()
    
    def reset(self):
        self.current = self.start
    
    def __iter__(self):
        # 每次返回新的迭代器,保持原始对象可重置
        return ResettableRangeIterator(self)

class ResettableRangeIterator:
    def __init__(self, range_obj):
        self.range_obj = range_obj
        self.current = range_obj.start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current >= self.range_obj.end:
            raise StopIteration
        value = self.current
        self.current += 1
        return value

# 使用示例
rrange = ResettableRange(1, 4)
print("第一次迭代:")
for num in rrange:
    print(num)  # 输出: 1, 2, 3

print("第二次迭代:")
for num in rrange:  # 自动重置
    print(num)  # 输出: 1, 2, 3

__iter__ 方法是 Python 迭代协议的核心,它使得自定义对象能够无缝集成到 Python 的迭代生态系统中。通过合理实现这个方法,可以创建各种强大的迭代器,从简单的序列迭代到复杂的数据流处理,大大提升了代码的灵活性和可读性。

object.__reversed__(self)

功能特性

__reversed__ 是用于实现对序列进行反向迭代的特殊方法,它允许对象自定义被 reversed() 内置函数调用时的行为。虽然在 object 层面并未直接定义 __reversed__,但它可以通过其他对象(如列表、元组等)的实现来支持反向迭代。

特点:

  • 当调用 reversed(obj) 时自动调用
  • 必须返回一个迭代器对象,按相反顺序产生元素
  • 为对象提供自定义的反向迭代逻辑
  • 是可选的,如果未实现但对象实现了 __len____getitem__reversed() 会尝试自动创建反向迭代器
  • 常用于优化反向迭代性能或实现特殊的反向逻辑

应用示例

自定义序列的反向迭代

为自定义序列类实现高效的反向迭代。

class CustomSequence:
    def __init__(self, data):
        self.data = list(data)
    
    def __len__(self):
        return len(self.data)
    
    def __getitem__(self, index):
        return self.data[index]
    
    def __reversed__(self):
        """返回反向迭代器"""
        # 方法1:使用生成器
        for i in range(len(self.data) - 1, -1, -1):
            yield self.data[i]
        
        # 方法2:也可以返回 reversed(self.data)
        # return reversed(self.data)

# 使用示例
seq = CustomSequence([1, 2, 3, 4, 5])
print("正向迭代:")
for item in seq:
    print(item, end=" ")  # 输出: 1 2 3 4 5
print()

print("反向迭代:")
for item in reversed(seq):
    print(item, end=" ")  # 输出: 5 4 3 2 1
print()

# 也可以手动获取反向迭代器
rev_iter = reversed(seq)
print("手动使用迭代器:", list(rev_iter))  # 输出: [5, 4, 3, 2, 1]

注意事项

与自动反向迭代的兼容性

如果对象没有实现 __reversed__ 但实现了 __len____getitem__,Python 会自动创建反向迭代器。

class AutoReversible:
    def __init__(self, data):
        self.data = data
    
    def __len__(self):
        return len(self.data)
    
    def __getitem__(self, index):
        return self.data[index]

# 即使没有实现 __reversed__,也能使用 reversed()
auto_seq = AutoReversible(['a', 'b', 'c', 'd'])
print("自动反向迭代:")
for item in reversed(auto_seq):
    print(item, end=" ")  # 输出: d c b a
print()
性能优化考虑

对于大型数据结构,考虑实现高效的反向迭代。

class EfficientReversal:
    def __init__(self, size):
        self.size = size
    
    def __len__(self):
        return self.size
    
    def __getitem__(self, index):
        if 0 <= index < self.size:
            return index * index  # 示例:返回平方数
        raise IndexError
    
    def __reversed__(self):
        # 高效实现:避免创建中间列表
        for i in range(self.size - 1, -1, -1):
            yield i * i

# 使用示例
efficient = EfficientReversal(5)
print("高效反向迭代:")
for item in reversed(efficient):
    print(item, end=" ")  # 输出: 16 9 4 1 0
print()

object.__contains__(self, item)

功能特性

__contains__ 可以让自定义对象可以支持成员包含操作(in 操作符),例如 item in obj。它接收两个参数:self (类的实例) 和 item (要检查的元素),并返回一个布尔值,指示 item 是否存在于 self 中。

应用示例

自定义集合类

创建一个简单的自定义集合,支持 in 操作符:

class NumberSet:
    def __init__(self, numbers):
        # 将输入的数字列表转换为集合进行存储
        self.numbers = set(numbers)
    
    def __contains__(self, item):
        # 检查数字是否在集合中
        return item in self.numbers

# 使用示例
my_set = NumberSet([1, 3, 5, 7, 9])

print(3 in my_set)    # 输出: True
print(4 in my_set)    # 输出: False
print(7 not in my_set) # 输出: False

注意事项

类型检查的重要性

在实现 __contains__ 时,记得处理不同类型的数据:

class SmartContainer:
    def __init__(self, items):
        self.items = list(items)
    
    def __contains__(self, item):
        # 先进行类型检查,避免意外错误
        if not isinstance(item, (int, float)):
            return False
        return item in self.items

container = SmartContainer([1, 2, 3])
print(2 in container)      # 输出: True
print("2" in container)    # 输出: False (字符串不被接受)
__iter__ 的关系

如果对象没有实现 __contains__,Python 会尝试通过迭代来检查成员关系:

class FallbackContainer:
    def __init__(self, items):
        self.items = items
    
    def __iter__(self):
        return iter(self.items)

# 即使没有 __contains__,也能使用 in 操作符
container = FallbackContainer([1, 2, 3])
print(2 in container)  # 输出: True (通过迭代查找)

不过,直接实现 __contains__ 通常会更高效,特别是对于大型数据集。

性能考虑

对于大型集合,考虑使用更高效的数据结构:

class EfficientContainer:
    def __init__(self, items):
        # 使用集合而不是列表,查找更快
        self.items = set(items)
    
    def __contains__(self, item):
        # 集合的成员测试是 O(1) 时间复杂度
        return item in self.items
错误处理

确保你的 __contains__ 方法能够优雅地处理异常情况:

class RobustContainer:
    def __init__(self, items):
        self.items = items
    
    def __contains__(self, item):
        try:
            # 尝试进行成员测试
            return item in self.items
        except TypeError:
            # 如果类型不兼容,返回 False
            return False

通过实现 __contains__ 方法,可以让自定义的类支持 Python 的 in 操作符,使代码更加直观和 Pythonic。

小结

魔术方法 功能 应用
object.__call__(self[, args...]) 使对象可以像函数一样被调用 创建可调用对象、函数装饰器、实现闭包行为
object.__len__(self) 返回对象的长度或大小 自定义容器类、集合对象、支持布尔判断
object.__length_hint__(self) 为迭代器提供长度预估 内存预分配优化、流式数据处理预估
object.__getitem__(self, key) 实现对象的索引访问 自定义序列、字典风格访问、多维数据结构
object.__setitem__(self, key, value) 实现对象的索引赋值 可变容器、配置管理、矩阵赋值
object.__delitem__(self, key) 实现对象的索引删除 序列元素删除、字典键删除、层级数据删除
object.__missing__(self, key) 处理字典中不存在的键访问 默认值字典、自动转换键、计算值字典
object.__iter__(self) 返回对象的迭代器 自定义序列迭代、数据流处理、过滤转换
object.__reversed__(self) 返回对象的反向迭代器 自定义序列反向、链表反向、树结构反向遍历
object.__contains__(self, item) 实现成员关系测试 自定义集合、范围检查、模式匹配

模拟数值类型

object.__add__(self, other)

功能特性

__add__ 是 Python 中用于实现加法操作的特殊方法,它允许自定义对象支持 + 操作符。当在代码中使用 obj1 + obj2 这样的表达式时,Python 会自动调用左边对象的 __add__ 方法。

这个方法是 Python 运算符重载功能的重要组成部分,使得自定义的类能够像内置类型一样支持直观的数学运算。通过实现 __add__,可以让对象之间进行有意义的"相加"操作,无论是数值计算、数据合并还是其他形式的组合。

应用示例

自定义向量的加法

创建支持向量加法的数学类:

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 __repr__(self):
        return f"Vector({self.x}, {self.y})"

# 使用示例
v1 = Vector(2, 3)
v2 = Vector(1, 4)
result = v1 + v2
print(result)  # 输出: Vector(3, 7)

这个例子展示了如何为二维向量实现加法运算,让向量相加的代码变得直观且符合数学直觉。

注意事项

类型检查的重要性

在实现 __add__ 时,应该考虑类型兼容性:

class SafeAdder:
    def __init__(self, value):
        self.value = value
    
    def __add__(self, other):
        # 确保other是相同类型
        if not isinstance(other, SafeAdder):
            return NotImplemented
        return SafeAdder(self.value + other.value)

使用 NotImplemented 而不是抛出异常,可以让 Python 尝试调用右边对象的 __radd__ 方法。

不可变对象的设计原则

加法操作应该返回新对象而不是修改原对象:

class ImmutablePoint:
    def __init__(self, x, y):
        self._x = x
        self._y = y
    
    def __add__(self, other):
        # 返回新对象,保持不可变性
        return ImmutablePoint(self._x + other._x, self._y + other._y)

这种设计符合直觉,因为数值加法在数学中就是产生新值而不改变原值。

__radd__ 的配合

当左边对象没有实现 __add__ 或返回 NotImplemented 时,Python 会尝试调用右边对象的 __radd__

class RightSideAdd:
    def __init__(self, value):
        self.value = value
    
    def __add__(self, other):
        # 正常加法
        return RightSideAdd(self.value + other)
    
    def __radd__(self, other):
        # 右加法:当对象在+右边时调用
        return RightSideAdd(other + self.value)
性能考虑

对于频繁的加法操作,考虑优化新对象的创建:

class OptimizedAdder:
    def __init__(self, data):
        self.data = data
    
    def __add__(self, other):
        # 对于大型数据结构,使用更高效的方法
        if isinstance(other, OptimizedAdder):
            return OptimizedAdder(self.data + other.data)
        return NotImplemented

object.__sub__(self, other)

功能特性

__sub__ 是 Python 中用于实现减法操作的特殊方法,它允许自定义对象支持 - 操作符。当在代码中使用 obj1 - obj2 这样的表达式时,Python 会自动调用左边对象的 __sub__ 方法。

这个方法是 Python 运算符重载功能的关键组成部分,使得自定义的类能够像内置数值类型一样支持直观的减法运算。通过实现 __sub__,可以让对象之间进行有意义的"相减"操作,无论是数学计算、集合操作还是其他形式的差异比较。

应用示例

数学向量的减法

创建支持向量减法的数学类:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __sub__(self, other):
        # 向量减法:对应分量相减
        return Vector(self.x - other.x, self.y - other.y)
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

# 使用示例
v1 = Vector(5, 8)
v2 = Vector(2, 3)
result = v1 - v2
print(result)  # 输出: Vector(3, 5)

注意事项

类型兼容性检查

在实现 __sub__ 时,应该考虑操作数的类型兼容性:

class SafeSubtract:
    def __init__(self, value):
        self.value = value
    
    def __sub__(self, other):
        # 检查类型兼容性
        if not isinstance(other, (SafeSubtract, int, float)):
            return NotImplemented
        return SafeSubtract(self.value - getattr(other, 'value', other))

使用 NotImplemented 可以让 Python 尝试调用右边对象的 __rsub__ 方法。

__rsub__ 的配合

当左边对象没有实现 __sub__ 或返回 NotImplemented 时,Python 会尝试调用右边对象的 __rsub__

class RightSubtract:
    def __init__(self, value):
        self.value = value
    
    def __sub__(self, other):
        return RightSubtract(self.value - other)
    
    def __rsub__(self, other):
        # 当对象在减法右边时调用
        return RightSubtract(other - self.value)
不可变设计原则

减法操作通常应该返回新对象而不是修改原对象:

class ImmutableValue:
    def __init__(self, value):
        self._value = value
    
    def __sub__(self, other):
        # 返回新对象,保持不可变性
        other_value = other._value if isinstance(other, ImmutableValue) else other
        return ImmutableValue(self._value - other_value)

这种设计符合数学运算的直觉,因为减法通常产生新值而不改变原值。

边界条件处理

对于可能产生无效结果的减法操作,应该包含适当的检查:

class NonNegativeValue:
    def __init__(self, value):
        self.value = max(0, value)  # 确保值非负
    
    def __sub__(self, other):
        other_value = other.value if isinstance(other, NonNegativeValue) else other
        # 确保结果不会变成负数
        return NonNegativeValue(max(0, self.value - other_value))

object.__mul__(self, other)

功能特性

__mul__ 是 Python 中用于实现乘法操作的特殊方法,它让对象能够响应 * 操作符。当在代码中使用 obj1 * obj2 这样的表达式时,Python 会自动调用左边对象的 __mul__ 方法。

这个方法的核心作用是定义对象在乘法运算中的行为。与数学中的乘法类似,在不同类型的对象中,乘法可以表示不同的操作:可能是数值相乘,也可能是重复操作,还可能是其他自定义的乘法语义。

应用示例

数学向量的标量乘法

实现向量与标量的乘法运算:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __mul__(self, scalar):
        # 向量与标量相乘,返回新向量
        if isinstance(scalar, (int, float)):
            return Vector(self.x * scalar, self.y * scalar)
        return NotImplemented
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

# 使用示例
v = Vector(2, 3)
result = v * 3
print(result)  # 输出: Vector(6, 9)

这个例子展示了如何为几何向量实现标量乘法。当向量乘以数字时,每个分量都按比例缩放,这是线性代数中的基本操作。

序列的重复操作

实现序列对象的重复操作:

class CustomString:
    def __init__(self, text):
        self.text = str(text)
    
    def __mul__(self, n):
        # 字符串重复n次
        if isinstance(n, int):
            return CustomString(self.text * n)
        return NotImplemented
    
    def __repr__(self):
        return f"'{self.text}'"

# 使用示例
greeting = CustomString("Hello! ")
repeated = greeting * 3
print(repeated)  # 输出: 'Hello! Hello! Hello! '

这个模式模仿了 Python 内置字符串和列表的乘法行为,通过乘法实现快速重复,在处理文本生成或模式重复时非常有用。

注意事项

处理不支持的类型

当遇到不支持的类型时,应该返回 NotImplemented

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __mul__(self, other):
        if isinstance(other, (int, float, CustomNumber)):
            # 处理支持的类型
            other_val = other.value if isinstance(other, CustomNumber) else other
            return CustomNumber(self.value * other_val)
        # 不支持的类型返回 NotImplemented
        return NotImplemented

这样 Python 会尝试调用右边对象的 __rmul__ 方法,或者抛出 TypeError

__rmul__ 的配合

当左操作数没有实现 __mul__ 或返回 NotImplemented 时,Python 会尝试右操作数的 __rmul__ 方法:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __mul__(self, other):
        # 正常乘法
        return CustomNumber(self.value * other)
    
    def __rmul__(self, other):
        # 反向乘法:other * self
        return CustomNumber(self.value * other)

# 使用示例
num = CustomNumber(5)
result1 = num * 3    # 调用 __mul__
result2 = 3 * num    # 调用 __rmul__
不可变对象的设计

乘法操作通常应该返回新对象,而不是修改原对象:

class ImmutableVector:
    def __init__(self, x, y):
        self._x = x
        self._y = y
    
    def __mul__(self, scalar):
        # 返回新对象,保持原对象不变
        return ImmutableVector(self._x * scalar, self._y * scalar)

这种设计符合大多数用户的预期,避免意外的副作用。

object.__matmul__(self, other)

功能特性

__matmul__ 是 Python 中用于实现矩阵乘法操作的特殊方法,它让对象能够响应 @ 操作符。这个操作符在 Python 3.5 中引入,专门用于表示矩阵乘法,以区别于元素级乘法。

与普通的 * 操作符不同,@ 操作符专门为线性代数中的矩阵乘法设计,在科学计算和机器学习领域具有重要意义。当在代码中使用 obj1 @ obj2 这样的表达式时,Python 会自动调用左边对象的 __matmul__ 方法。在 NumPy、PyTorch 等科学计算库中广泛使用,与 __rmatmul__ 配合实现反向矩阵乘法。

应用示例

矩阵乘法运算

实现标准的矩阵乘法运算:

class Matrix:
    def __init__(self, data):
        self.data = data
        self.rows = len(data)
        self.cols = len(data[0]) if data else 0
    
    def __matmul__(self, other):
        # 标准的矩阵乘法实现
        if not isinstance(other, Matrix):
            return NotImplemented
        
        if self.cols != other.rows:
            raise ValueError("矩阵维度不匹配")
        
        result = []
        for i in range(self.rows):
            row = []
            for j in range(other.cols):
                # 计算点积
                total = sum(self.data[i][k] * other.data[k][j] 
                           for k in range(self.cols))
                row.append(total)
            result.append(row)
        return Matrix(result)
    
    def __repr__(self):
        return '\n'.join(' '.join(f'{val:4}' for val in row) 
                        for row in self.data)

# 使用示例
A = Matrix([[1, 2], [3, 4]])
B = Matrix([[2, 0], [1, 2]])
result = A @ B  # 矩阵乘法
print(result)
# 输出:
#    4    4
#   10    8

这个例子展示了线性代数中的标准矩阵乘法,这是科学计算、图形处理和机器学习中的基础运算。

注意事项

__mul__ 的区别

@ 操作符与 * 操作符有明确的语义区分:

class DualOperationMatrix:
    def __init__(self, data):
        self.data = data
    
    def __mul__(self, other):
        # 元素级乘法(Hadamard积)
        if isinstance(other, DualOperationMatrix):
            return DualOperationMatrix(
                [[self.data[i][j] * other.data[i][j] 
                  for j in range(len(self.data[0]))]
                 for i in range(len(self.data))]
            )
        return NotImplemented
    
    def __matmul__(self, other):
        # 矩阵乘法
        if isinstance(other, DualOperationMatrix):
            # 标准矩阵乘法实现...
            pass
        return NotImplemented

在科学计算中,这种区分很重要:* 表示元素级乘法,@ 表示矩阵乘法。

维度匹配要求

矩阵乘法有严格的维度要求:

class RobustMatrix:
    def __matmul__(self, other):
        if not isinstance(other, RobustMatrix):
            return NotImplemented
        
        if self.cols != other.rows:
            raise ValueError(
                f"矩阵维度不匹配: ({self.rows}x{self.cols}) @ "
                f"({other.rows}x{other.cols})"
            )
        # ... 乘法实现

第一个矩阵的列数必须等于第二个矩阵的行数,否则运算无法进行。

性能考虑

对于大型矩阵乘法,简单的三重循环实现效率很低:

class EfficientMatrix:
    def __matmul__(self, other):
        # 实际应用中会使用优化算法:
        # - 分块乘法缓存友好
        # - Strassen算法降低复杂度
        # - 使用BLAS等优化库
        pass

在实际的科学计算库中,矩阵乘法会使用高度优化的算法来提升性能。

__rmatmul__ 的配合

当左操作数不支持 @ 操作时,需要实现反向版本:

class Matrix:
    def __matmul__(self, other):
        # 正常矩阵乘法
        pass
    
    def __rmatmul__(self, other):
        # 处理 other @ matrix 的情况
        # 通常需要将other转换为矩阵,或者返回NotImplemented
        return NotImplemented

object.__truediv__(self, other)

功能特性

__truediv__ 是 Python 中用于实现真除法操作的特殊方法,它让对象能够响应 / 操作符。与地板除法 // 不同,真除法会保留小数部分,产生更精确的除法结果。在 Python 3 中,/ 操作符总是执行真除法,即使操作数都是整数也会返回浮点数结果。这种方法专门用于需要精确除法运算的场景,如科学计算、工程应用和数据分析。

应用示例

数学向量的标量除法

实现向量与标量的除法运算:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __truediv__(self, scalar):
        # 向量除以标量,返回新向量
        if isinstance(scalar, (int, float)):
            if scalar == 0:
                raise ZeroDivisionError("不能除以零")
            return Vector(self.x / scalar, self.y / scalar)
        return NotImplemented
    
    def __repr__(self):
        return f"Vector({self.x:.2f}, {self.y:.2f})"

# 使用示例
v = Vector(6.0, 9.0)
result = v / 3
print(result)  # 输出: Vector(2.00, 3.00)

注意事项

除零错误处理

除法运算必须处理除零的情况:

class SafeDivisible:
    def __init__(self, value):
        self.value = value
    
    def __truediv__(self, other):
        if isinstance(other, (int, float)):
            if other == 0:
                # 可以选择返回特殊值或抛出异常
                raise ZeroDivisionError("除零错误")
            return SafeDivisible(self.value / other)
        return NotImplemented
类型检查和 NotImplemented

正确处理不支持的类型:

class TypedDivisible:
    def __init__(self, value):
        self.value = value
    
    def __truediv__(self, other):
        if isinstance(other, (int, float, TypedDivisible)):
            other_val = other.value if isinstance(other, TypedDivisible) else other
            if other_val == 0:
                raise ZeroDivisionError("除零错误")
            return TypedDivisible(self.value / other_val)
        # 不支持的类型返回 NotImplemented
        return NotImplemented
__rtruediv__ 的配合

当左操作数不支持 / 操作时,需要实现反向版本:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __truediv__(self, other):
        # 正常除法
        return CustomNumber(self.value / other)
    
    def __rtruediv__(self, other):
        # 处理 other / custom_number 的情况
        return CustomNumber(other / self.value)
浮点数精度问题

真除法使用浮点数,需要注意精度问题:

class PreciseDivision:
    def __truediv__(self, other):
        # 对于需要高精度的场景,考虑使用 decimal 模块
        from decimal import Decimal, getcontext
        getcontext().prec = 10  # 设置精度
        result = Decimal(self.value) / Decimal(other)
        return float(result)  # 或保持 Decimal 类型

object.__floordiv__(self, other)

功能特性

__floordiv__ 是 Python 中用于实现地板除法操作的特殊方法,它让对象能够响应 // 操作符。地板除法也称为整数除法,它会将结果向下取整到最接近的整数。与真除法 / 不同,地板除法 // 不保留小数部分,而是返回不大于结果的最大整数。这种方法在需要整数结果的场景中特别有用,如分组计算、离散数学和资源分配等问题。

应用示例

离散数学中的整数除法

实现整数的地板除法运算:

class DiscreteValue:
    def __init__(self, value):
        self.value = value
    
    def __floordiv__(self, divisor):
        # 执行地板除法,结果向下取整
        if isinstance(divisor, (int, float)):
            if divisor == 0:
                raise ZeroDivisionError("不能除以零")
            return DiscreteValue(self.value // divisor)
        return NotImplemented
    
    def __repr__(self):
        return f"DiscreteValue({self.value})"

# 使用示例
num = DiscreteValue(17)
result = num // 5
print(result)  # 输出: DiscreteValue(3)

这个例子展示了基本的地板除法运算,17 除以 5 的结果是 3.4,向下取整后得到 3。

注意事项

负数的地板除法

地板除法对负数的处理可能与直觉不同:

# 负数的地板除法示例
print(7 // 2)   # 输出: 3
print(-7 // 2)  # 输出: -4 (不是 -3)

地板除法总是向负无穷方向取整,对于负数结果会比真除法结果更小。

除零错误处理

地板除法同样需要处理除零的情况:

class SafeFloorDiv:
    def __init__(self, value):
        self.value = value
    
    def __floordiv__(self, other):
        if isinstance(other, (int, float)):
            if other == 0:
                raise ZeroDivisionError("地板除法除零错误")
            return SafeFloorDiv(self.value // other)
        return NotImplemented
类型检查和 NotImplemented

正确处理不支持的类型:

class TypedFloorDiv:
    def __init__(self, value):
        self.value = value
    
    def __floordiv__(self, other):
        if isinstance(other, (int, float, TypedFloorDiv)):
            other_val = other.value if isinstance(other, TypedFloorDiv) else other
            if other_val == 0:
                raise ZeroDivisionError("除零错误")
            return TypedFloorDiv(self.value // other_val)
        return NotImplemented
__rfloordiv__ 的配合

当左操作数不支持 // 操作时,需要实现反向版本:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __floordiv__(self, other):
        return CustomNumber(self.value // other)
    
    def __rfloordiv__(self, other):
        # 处理 other // custom_number 的情况
        return CustomNumber(other // self.value)
返回值类型

地板除法的返回值类型取决于操作数类型:

# 整数地板除法返回整数
print(type(7 // 2))    # 输出: <class 'int'>

# 浮点数地板除法返回浮点数(但值是整数)
print(type(7.0 // 2))  # 输出: <class 'float'>

object.__mod__(self, other)

功能特性

__mod__ 是 Python 中用于实现取模运算的特殊方法,它让对象能够响应 % 操作符。取模运算计算的是除法后的余数,是编程中极为实用的数学运算。

在Python中,取模运算总是返回非负的余数,这与数学定义略有不同,但符合Python的运算规则。

应用示例

整数取模运算

使用取模运算实现高效的循环缓冲区:

class ModExample:
    def __init__(self, value):
        self.value = value
    
    def __mod__(self, other):
        return self.value % other

# 使用示例
a = ModExample(10)
b = 3
result = a % b
print(result)  # 输出: 1

注意事项

负数的取模行为

Python 的取模运算对于负数的处理遵循数学定义:

# 正数的取模
print(7 % 3)   # 输出: 1

# 负数的取模  
print(-7 % 3)  # 输出: 2

结果总是非负的,并且满足 (a // b) * b + (a % b) == a 的数学关系。

除零错误处理

取模运算必须处理除数为零的情况:

class SafeModuloOperation:
    def __init__(self, value):
        self.value = value
    
    def __mod__(self, other):
        if isinstance(other, (int, float)):
            if other == 0:
                raise ZeroDivisionError("取模运算的除数不能为零")
            return SafeModuloOperation(self.value % other)
        return NotImplemented
浮点数精度问题

浮点数的取模运算可能受到精度影响:

# 浮点数取模的精度问题
result = 10.2 % 3.1
print(f"{result:.10f}")  # 输出: 0.8999999999

在实际应用中,可能需要考虑浮点数的精度容错。

__divmod__ 的关系

__mod____divmod__ 方法密切相关:

class DivModExample:
    def __init__(self, value):
        self.value = value
    
    def __mod__(self, other):
        # 取模运算
        return self.value % other
    
    def __divmod__(self, other):
        # 同时返回商和余数
        quotient = self.value // other
        remainder = self.value % other
        return quotient, remainder

example = DivModExample(17)
print(example % 5)        # 输出: 2
print(divmod(example, 5)) # 输出: (3, 2)

__divmod__ 方法可以同时获得商和余数,比分别调用 //% 更高效。

object.__pow__(self, other[, modulo])

功能特性

__pow__ 是 Python 中用于实现幂运算的特殊方法,它让对象能够响应 ** 操作符和内置的 pow() 函数。幂运算在数学中表示重复乘法,是许多科学计算和工程应用的基础运算。幂运算的核心概念是指数增长和几何级数。与加法和乘法不同,幂运算描述的是更快速的增长模式,这使得它在复利计算、物理定律、几何放大等领域有着不可替代的作用。Python 的幂运算支持两种形式:基本的二元幂运算和高效的三元模幂运算。

应用示例

数学向量和矩阵的幂运算

实现向量的标量幂运算:

class MathVector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __pow__(self, exponent):
        # 向量的分量分别进行幂运算
        if isinstance(exponent, (int, float)):
            return MathVector(self.x ** exponent, self.y ** exponent)
        return NotImplemented
    
    def magnitude(self):
        # 计算向量的模长
        return (self.x ** 2 + self.y ** 2) ** 0.5
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

# 使用示例
v = MathVector(3, 4)
squared = v ** 2
print(f"原向量: {v}, 模长: {v.magnitude():.2f}")
print(f"平方后: {squared}")  # 输出: Vector(9, 16)

这个例子展示了向量分量的幂运算,在几何变换和物理计算中很常见。

注意事项

三元形式的模幂运算

__pow__ 方法可以接受可选的第三个参数用于模幂运算:

class EfficientModPow:
    def __init__(self, value):
        self.value = value
    
    def __pow__(self, other, modulo=None):
        if modulo is None:
            # 普通幂运算
            return EfficientModPow(self.value ** other)
        else:
            # 模幂运算:更高效的计算 (value^other) mod modulo
            return EfficientModPow(pow(self.value, other, modulo))
负指数和分数指数的处理

需要考虑各种指数情况:

class ComprehensivePower:
    def __init__(self, value):
        self.value = value
    
    def __pow__(self, exponent):
        if isinstance(exponent, (int, float)):
            try:
                # 处理负指数(倒数)和分数指数(开方)
                result = self.value ** exponent
                return ComprehensivePower(result)
            except (ZeroDivisionError, ValueError) as e:
                raise ValueError(f"幂运算错误: {e}")
        return NotImplemented
__rpow__ 的配合

当左操作数不支持 ** 操作时,需要实现反向版本:

class CustomBase:
    def __init__(self, value):
        self.value = value
    
    def __pow__(self, exponent):
        return CustomBase(self.value ** exponent)
    
    def __rpow__(self, other):
        # 处理 other ** custom_base 的情况
        return CustomBase(other ** self.value)
大数运算的性能考虑

对于大指数运算,需要考虑算法效率:

class OptimizedPower:
    def __pow__(self, exponent, modulo=None):
        if modulo is not None:
            # 使用快速模幂算法,时间复杂度O(log n)
            return self._fast_modular_pow(exponent, modulo)
        else:
            # 使用快速幂算法
            return self._fast_power(exponent)
错误处理边界情况

幂运算需要处理各种边界情况:

class SafePower:
    def __init__(self, value):
        self.value = value
    
    def __pow__(self, exponent):
        if isinstance(exponent, (int, float)):
            # 处理0的负指数幂等错误情况
            if self.value == 0 and exponent < 0:
                raise ZeroDivisionError("0的负指数幂未定义")
            # 处理负数的分数指数幂
            if self.value < 0 and isinstance(exponent, float):
                if exponent % 1 != 0:  # 非整数指数
                    raise ValueError("负数的非整数次幂可能产生复数")
            return SafePower(self.value ** exponent)
        return NotImplemented

__pow__ 方法为 Python 对象提供了强大的幂运算能力,在科学计算、金融建模、几何变换和密码学等领域有着广泛的应用。正确的实现需要考虑各种指数情况、性能优化和错误处理,确保运算结果既符合数学定义又满足实际应用需求。

object.__lshift__(self, other)

功能特性

__lshift__ 是 Python 中用于实现左移位运算的特殊方法,它让对象能够响应 << 操作符。左移位运算在底层编程和位操作中具有重要作用,主要用于对二进制位进行快速操作。左移位运算的核心原理是将数字的二进制表示向左移动指定的位数,右侧空出的位用0填充。这种运算在数学上等价于乘以2的幂次方,但相比乘法运算,移位运算在计算机底层执行效率更高。除了传统的位操作,左移位运算还可以被重载用于实现其他语义,如数据流操作。

应用示例

快速乘法运算

使用左移位实现快速的2的幂次方乘法:

class FastMultiplier:
    def __init__(self, value):
        self.value = value
    
    def __lshift__(self, power):
        # 左移n位等价于乘以2的n次方
        if isinstance(power, int):
            return FastMultiplier(self.value << power)
        return NotImplemented
    
    def __repr__(self):
        return f"值: {self.value}"

# 使用示例
number = FastMultiplier(5)
doubled = number << 1      # 5 * 2^1 = 10
quadrupled = number << 2   # 5 * 2^2 = 20
times_eight = number << 3  # 5 * 2^3 = 40

print(f"原始: {number}")       # 输出: 值: 5
print(f"乘以2: {doubled}")     # 输出: 值: 10
print(f"乘以4: {quadrupled}")  # 输出: 值: 20
print(f"乘以8: {times_eight}") # 输出: 值: 40

这个例子展示了左移位在快速乘法中的应用,相比普通的乘法运算,移位运算在底层执行效率更高。

数据流或管道操作

重载左移位用于数据流操作:

class DataPipeline:
    def __init__(self, data=None):
        self.data = data if data is not None else []
    
    def __lshift__(self, item):
        # 使用 << 操作符向管道添加数据
        if not isinstance(item, DataPipeline):
            self.data.append(item)
            return self
        else:
            # 如果是另一个管道,合并数据
            self.data.extend(item.data)
            return self
    
    def process(self):
        # 处理管道中的数据
        return [x * 2 if isinstance(x, (int, float)) else str(x).upper() 
                for x in self.data]
    
    def __repr__(self):
        return f"DataPipeline{self.data}"

# 使用示例
pipeline = DataPipeline()
pipeline << "hello" << 42 << "world"
result = pipeline.process()

print(f"管道内容: {pipeline}")  # 输出: DataPipeline['hello', 42, 'world']
print(f"处理结果: {result}")    # 输出: ['HELLO', 84, 'WORLD']

这个例子展示了左移位操作符的重载应用,可以用于创建流畅的数据处理管道接口。

注意事项

整数溢出处理

左移位可能导致整数溢出:

class SafeShift:
    def __init__(self, value, bit_width=32):
        self.value = value
        self.bit_width = bit_width
    
    def __lshift__(self, positions):
        if isinstance(positions, int):
            # 防止溢出,使用掩码限制位数
            mask = (1 << self.bit_width) - 1
            result = (self.value << positions) & mask
            return SafeShift(result, self.bit_width)
        return NotImplemented
负位移的处理

左移负位数通常没有明确定义:

class RobustShifter:
    def __lshift__(self, positions):
        if isinstance(positions, int):
            if positions < 0:
                # 可以定义为右移,或者抛出错误
                raise ValueError("左移位不支持负位移")
            # 正常左移操作...
        return NotImplemented
__rlshift__ 的配合

当左操作数不支持 << 操作时,需要实现反向版本:

class CustomShifter:
    def __init__(self, value):
        self.value = value
    
    def __lshift__(self, other):
        return CustomShifter(self.value << other)
    
    def __rlshift__(self, other):
        # 处理 other << custom_shifter 的情况
        return CustomShifter(other << self.value)
浮点数的移位限制

Python 的移位运算只适用于整数:

# 这些操作会抛出TypeError
# 3.14 << 2
# 5.0 << 1

如果需要支持浮点数,需要在 __lshift__ 方法中进行类型转换或特殊处理。

性能考虑

对于频繁的移位操作,考虑使用位运算优化:

class OptimizedBitOps:
    def __lshift__(self, n):
        # 对于已知范围的值,可以使用查表法等优化
        if n in self._shift_cache:
            return self._shift_cache[n]
        # 正常计算并缓存结果...

__lshift__ 方法为 Python 对象提供了强大的位操作能力,在底层编程、性能优化和数据处理等场景中有着重要应用。正确的实现需要考虑边界情况、类型安全和性能优化,确保运算结果既符合数学定义又满足实际应用需求。

object.__rshift__(self, other)

功能特性

__rshift__ 是 Python 中用于实现右移位运算的特殊方法,它让对象能够响应 >> 操作符。右移位运算在底层编程、数据分析和位操作中具有重要作用,主要用于对二进制位进行高效操作。右移位运算的核心原理是将数字的二进制表示向右移动指定的位数,左侧空出的位根据符号位填充。对于正整数,左侧用0填充;对于负整数,左侧用1填充。这种运算在数学上等价于除以2的幂次方并向下取整,相比除法运算在计算机底层执行效率更高。除了传统的位操作,右移位运算还可以被重载用于实现数据提取、流水线处理等语义。

应用示例

快速除法运算

使用右移位实现快速的2的幂次方除法:

class FastDivider:
    def __init__(self, value):
        self.value = value
    
    def __rshift__(self, power):
        if isinstance(power, int):
            return FastDivider(self.value >> power)
        return NotImplemented
    
    def __repr__(self):
        return f"值: {self.value}"

# 使用示例
number = FastDivider(40)
halved = number >> 1      # 40 / 2^1 = 20
quartered = number >> 2   # 40 / 2^2 = 10
print(f"原始: {number}")      # 输出: 值: 40
print(f"除以2: {halved}")     # 输出: 值: 20
print(f"除以4: {quartered}")  # 输出: 值: 10
数据流提取操作

重载右移位用于数据提取:

class DataStream:
    def __init__(self, data):
        self.data = data
        self.position = 0
    
    def __rshift__(self, count):
        if isinstance(count, int):
            # 提取指定数量的元素
            end_pos = self.position + count
            extracted = self.data[self.position:end_pos]
            self.position = end_pos
            return extracted
        return NotImplemented

# 使用示例
stream = DataStream([1, 2, 3, 4, 5, 6])
chunk1 = stream >> 2  # 提取2个元素
chunk2 = stream >> 3  # 再提取3个元素
print(f"第一块: {chunk1}")  # 输出: [1, 2]
print(f"第二块: {chunk2}")  # 输出: [3, 4, 5]

注意事项

负数的右移位行为

负数的右移位行为需要特别注意:

# 正数右移位
print(16 >> 2)   # 输出: 4

# 负数右移位(符号位扩展)
print(-16 >> 2)  # 输出: -4

负数右移位时会进行符号位扩展,保持数值的符号。

位移位数的限制

位移位数应该为非负整数:

class SafeShifter:
    def __rshift__(self, positions):
        if isinstance(positions, int):
            if positions < 0:
                raise ValueError("右移位不支持负位移")
            if positions > 1000:  # 合理的上限
                raise ValueError("位移位数过大")
            # 正常右移操作...
        return NotImplemented
__rrshift__ 的配合

当左操作数不支持 >> 操作时,需要实现反向版本:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __rshift__(self, other):
        return CustomNumber(self.value >> other)
    
    def __rrshift__(self, other):
        return CustomNumber(other >> self.value)
浮点数的处理

Python 的移位运算只支持整数类型:

# 这些操作会抛出TypeError
# 3.14 >> 2
# 5.0 >> 1

如果需要对浮点数进行类似操作,需要在 __rshift__ 方法中实现类型转换或特殊语义。

object.__and__(self, other)

功能特性

__and__ 是 Python 中用于实现按位与运算的特殊方法,它让对象能够响应 & 操作符。按位与运算是一种二进制位操作,只有当两个操作数的对应位都为1时,结果位才为1。按位与运算在计算机科学中有着广泛的应用,从简单的位掩码操作到复杂的权限检查系统。这种运算的核心原理是逻辑与操作在二进制位级别的实现,可以用于提取特定位、设置标志位和进行逻辑过滤。除了传统的位操作,& 操作符还可以被重载用于实现集合交集、逻辑过滤等语义。

应用示例

权限检查和位掩码

使用按位与进行权限验证:

class Permissions:
    def __init__(self, flags):
        self.flags = flags
    
    def __and__(self, other):
        if isinstance(other, (int, Permissions)):
            other_flags = other if isinstance(other, int) else other.flags
            return Permissions(self.flags & other_flags)
        return NotImplemented
    
    def has_permission(self, permission):
        return (self.flags & permission) == permission
    
    def __repr__(self):
        return f"权限: 0b{self.flags:04b}"

# 使用示例
READ = 0b0001
WRITE = 0b0010
EXECUTE = 0b0100

user_perms = Permissions(READ | WRITE)
required = READ

if user_perms & required:
    print("有读取权限")  # 输出: 有读取权限
集合交集操作

重载 & 操作符用于集合交集:

class NumberSet:
    def __init__(self, numbers):
        self.numbers = set(numbers)
    
    def __and__(self, other):
        if isinstance(other, NumberSet):
            return NumberSet(self.numbers & other.numbers)
        return NotImplemented
    
    def __repr__(self):
        return f"集合: {self.numbers}"

# 使用示例
set1 = NumberSet([1, 2, 3, 4])
set2 = NumberSet([3, 4, 5, 6])
intersection = set1 & set2
print(intersection)  # 输出: 集合: {3, 4}

注意事项

布尔与和按位与的区别

&and 操作符有重要区别:

# 按位与(&) - 位级操作
print(5 & 3)   # 输出: 1 (0b101 & 0b011 = 0b001)

# 逻辑与(and) - 布尔操作  
print(5 and 3) # 输出: 3 (返回最后一个真值)
__rand__ 的配合

当左操作数不支持 & 操作时,需要实现反向版本:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __and__(self, other):
        return CustomNumber(self.value & other)
    
    def __rand__(self, other):
        return CustomNumber(other & self.value)
类型一致性

确保与运算的结果类型一致:

class ConsistentAnd:
    def __init__(self, value):
        self.value = value
    
    def __and__(self, other):
        if isinstance(other, ConsistentAnd):
            return ConsistentAnd(self.value & other.value)
        elif isinstance(other, int):
            return ConsistentAnd(self.value & other)
        return NotImplemented
非整数类型的处理

对于非整数类型,需要明确定义与运算的语义:

class TextFilter:
    def __init__(self, text):
        self.text = text
    
    def __and__(self, pattern):
        # 定义字符串的"与"操作:包含模式
        if pattern in self.text:
            return TextFilter(self.text)
        return TextFilter("")

__and__ 方法为 Python 对象提供了灵活的与运算能力,在权限管理、集合操作、数据过滤和网络计算等场景中有着重要应用。正确的实现需要考虑运算语义、类型一致性和边界情况,确保运算结果既符合逻辑定义又满足实际应用需求。

object.__xor__(self, other)

功能特性

__xor__ 是 Python 中用于实现按位异或运算的特殊方法,它让对象能够响应 ^ 操作符。按位异或运算是一种二进制位操作,当两个操作数的对应位不同时,结果位为1;相同时,结果位为0。按位异或运算在计算机科学中具有独特的性质,它既是可逆的又是无进位的加法运算。这种运算的核心特性使其在加密算法、错误检测和数据交换等场景中特别有用。异或运算具有自反性:a ^ a = 0a ^ 0 = a,这些数学特性为各种算法提供了理论基础。

应用示例

集合对称差集

重载 ^ 操作符用于集合对称差集:

class NumberSet:
    def __init__(self, numbers):
        self.numbers = set(numbers)
    
    def __xor__(self, other):
        if isinstance(other, NumberSet):
            # 对称差集:在A或B中,但不同时在两者中
            return NumberSet(self.numbers ^ other.numbers)
        return NotImplemented
    
    def __repr__(self):
        return f"集合: {self.numbers}"

# 使用示例
set1 = NumberSet([1, 2, 3, 4])
set2 = NumberSet([3, 4, 5, 6])
symmetric_diff = set1 ^ set2
print(symmetric_diff)  # 输出: 集合: {1, 2, 5, 6}

注意事项

布尔异或和按位异或的区别

^!= 操作符有重要区别:

# 按位异或(^) - 位级操作
print(5 ^ 3)      # 输出: 6 (0b101 ^ 0b011 = 0b110)

# 逻辑不等(!=) - 布尔操作
print(5 != 3)     # 输出: True
__rxor__ 的配合

当左操作数不支持 ^ 操作时,需要实现反向版本:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __xor__(self, other):
        return CustomNumber(self.value ^ other)
    
    def __rxor__(self, other):
        return CustomNumber(other ^ self.value)
浮点数的处理

异或运算通常只适用于整数类型:

# 这些操作会抛出TypeError
# 3.14 ^ 2.71
# 5.0 ^ 2

如果需要对其他类型支持异或语义,需要在 __xor__ 方法中明确定义。

结合律和交换律

异或运算满足结合律和交换律:

# 交换律
a, b = 5, 3
print(a ^ b == b ^ a)  # 输出: True

# 结合律
c = 7
print((a ^ b) ^ c == a ^ (b ^ c))  # 输出: True

这些数学性质在算法设计中很有用。

object.__or__(self, other)

功能特性

__or__ 是 Python 中用于实现按位或运算的特殊方法,它让对象能够响应 | 操作符。按位或运算是一种二进制位操作,当两个操作数的对应位中至少有一个为1时,结果位就为1。按位或运算在计算机科学中用于组合多个标志或条件,是一种常见的位级操作技术。这种运算的核心特性是"包含性" - 只要任一操作数包含某个特征,结果就会包含该特征。除了传统的位操作,| 操作符还可以被重载用于实现集合并集、配置合并和条件组合等语义。

应用示例

集合并集操作

重载 | 操作符用于集合并集:

class NumberSet:
    def __init__(self, numbers):
        self.numbers = set(numbers)
    
    def __or__(self, other):
        if isinstance(other, NumberSet):
            return NumberSet(self.numbers | other.numbers)
        return NotImplemented
    
    def __repr__(self):
        return f"集合: {self.numbers}"

# 使用示例
set1 = NumberSet([1, 2, 3])
set2 = NumberSet([3, 4, 5])
union = set1 | set2
print(union)  # 输出: 集合: {1, 2, 3, 4, 5}

注意事项

布尔或和按位或的区别

|or 操作符有重要区别:

# 按位或(|) - 位级操作
print(5 | 3)      # 输出: 7 (0b101 | 0b011 = 0b111)

# 逻辑或(or) - 布尔操作,短路求值
print(5 or 3)     # 输出: 5 (返回第一个真值)
__ror__ 的配合

当左操作数不支持 | 操作时,需要实现反向版本:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __or__(self, other):
        return CustomNumber(self.value | other)
    
    def __ror__(self, other):
        return CustomNumber(other | self.value)
结合律特性

或运算满足结合律,顺序不影响结果:

a, b, c = 1, 2, 4
print((a | b) | c == a | (b | c))  # 输出: True

这个特性在组合多个选项时很有用。

幂等性

或运算具有幂等性:

value = 5
print(value | value == value)  # 输出: True

相同的值进行或运算不会改变结果。

object.__neg__(self)

功能特性

__neg__ 是 Python 中用于实现一元负号运算的特殊方法,它让对象能够响应 - 操作符。与二元运算符不同,一元负号运算只需要一个操作数,用于改变数值的符号或实现对象的反向操作。一元负号运算在数学中表示取相反数,在编程中这个概念可以扩展到各种需要"反向"或"对立"操作的场景。这种方法的核心作用是定义对象在被负号操作时的行为,无论是数值取反、方向反转还是状态取反,都能通过统一的语法来表达。

应用示例

数学向量的反向

实现向量的反向操作:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __neg__(self):
        # 向量取反:各分量取相反数
        return Vector(-self.x, -self.y)
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

# 使用示例
v = Vector(3, -4)
reversed_v = -v
print(f"原向量: {v}")         # 输出: Vector(3, -4)
print(f"反向向量: {reversed_v}") # 输出: Vector(-3, 4)
自定义数值类型的符号操作

为自定义数值类型实现符号操作:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __neg__(self):
        # 数值取反
        return CustomNumber(-self.value)
    
    def __repr__(self):
        return f"CustomNumber({self.value})"

# 使用示例
num = CustomNumber(10)
negative_num = -num
print(f"原数: {num}")           # 输出: CustomNumber(10)
print(f"负数: {negative_num}")   # 输出: CustomNumber(-10)

注意事项

不可变对象设计

负号操作通常应该返回新对象:

class ImmutableVector:
    def __init__(self, x, y):
        self._x = x
        self._y = y
    
    def __neg__(self):
        # 返回新对象,保持原对象不变
        return ImmutableVector(-self._x, -self._y)
__pos__ 的对应关系

负号操作通常与正号操作对应:

class SignedNumber:
    def __init__(self, value):
        self.value = value
    
    def __neg__(self):
        return SignedNumber(-self.value)
    
    def __pos__(self):
        return SignedNumber(+self.value)  # 通常返回自身或副本
非数值对象的语义定义

对于非数值对象,需要明确定义负号的语义:

class State:
    def __init__(self, active):
        self.active = active
    
    def __neg__(self):
        # 状态取反:激活状态切换
        return State(not self.active)
错误处理

考虑不支持负号操作的情况:

class NonNegative:
    def __init__(self, value):
        if value < 0:
            raise ValueError("只接受非负值")
        self.value = value
    
    def __neg__(self):
        # 可以选择抛出错误或返回特殊值
        raise TypeError("非负对象不支持负号操作")

object.__pos__(self)

功能特性

__pos__ 是 Python 中用于实现一元正号运算的特殊方法,它让对象能够响应 + 操作符。与二元加法运算符不同,一元正号运算只需要一个操作数,通常用于保持数值的符号不变或实现对象的正向操作。一元正号运算在数学中通常被视为恒等操作,即 +x 等于 x。然而在编程中,这个方法提供了机会来定义对象在被正号操作时的特定行为。虽然对于简单数值类型来说,正号操作看似多余,但对于自定义对象来说,它可以用于验证、标准化或创建对象的正向版本。

应用示例

数值验证和标准化

使用正号操作进行数值验证:

class ValidatedNumber:
    def __init__(self, value):
        self.value = value
    
    def __pos__(self):
        # 正号操作可以用于数值验证
        if self.value < 0:
            raise ValueError("期望正数")
        return ValidatedNumber(self.value)
    
    def __repr__(self):
        return f"ValidatedNumber({self.value})"

# 使用示例
positive_num = ValidatedNumber(5)
validated = +positive_num  # 验证通过
print(validated)  # 输出: ValidatedNumber(5)

注意事项

__neg__ 的对称性

正号操作通常应该与负号操作保持逻辑对称:

class SymmetricNumber:
    def __init__(self, value):
        self.value = value
    
    def __pos__(self):
        return SymmetricNumber(+self.value)
    
    def __neg__(self):
        return SymmetricNumber(-self.value)
不可变对象处理

正号操作通常应该返回新对象:

class ImmutableValue:
    def __init__(self, value):
        self._value = value
    
    def __pos__(self):
        # 返回新对象而不是修改原对象
        return ImmutableValue(self._value)
默认行为

如果没有实现 __pos__ 方法,Python 不会自动提供默认实现:

class NoPosImplementation:
    def __init__(self, value):
        self.value = value

obj = NoPosImplementation(5)
# +obj  # 这会抛出 TypeError
语义一致性

确保正号操作的语义在应用上下文中是一致的:

class ConsistentPositive:
    def __pos__(self):
        # 确保在不同情况下行为一致
        # 避免有时返回自身,有时返回新对象
        return type(self)(self.validated_value())

object.__abs__(self)

功能特性

__abs__ 是 Python 中用于实现绝对值运算的特殊方法,它让对象能够响应内置的 abs() 函数。绝对值运算在数学中表示一个数到零点的距离,总是返回非负值。绝对值运算的核心概念是"大小"或"量级"的度量,而不考虑方向或符号。在编程中,这个概念可以扩展到各种需要度量"大小"、"长度"或"强度"的场景。对于自定义对象来说,绝对值操作可以表示对象的模、范数、大小或其他无方向的量度。

应用示例

向量的模长计算

实现向量的模长(大小)计算:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __abs__(self):
        # 计算向量的模长:√(x² + y²)
        return (self.x**2 + self.y**2)**0.5
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

# 使用示例
v = Vector(3, 4)
magnitude = abs(v)
print(f"向量: {v}")       # 输出: Vector(3, 4)
print(f"模长: {magnitude}") # 输出: 5.0

注意事项

返回值类型

绝对值操作应该返回非负值:

class ProperAbs:
    def __init__(self, value):
        self.value = value
    
    def __abs__(self):
        # 确保返回非负值
        result = abs(self.value)
        if result < 0:
            # 这通常不会发生,但可以作为安全检查
            return 0
        return result
与符号操作的区分

绝对值操作应该专注于大小,不考虑符号:

class ClearSemantics:
    def __init__(self, value):
        self.value = value
    
    def __abs__(self):
        # 只关心大小,不关心方向
        return abs(self.value)
    
    def __neg__(self):
        # 改变方向,但保持大小
        return ClearSemantics(-self.value)
数学一致性

绝对值操作应该满足数学性质:

# 绝对值的基本性质
print(abs(-5) == abs(5))    # 输出: True
print(abs(0) == 0)          # 输出: True
print(abs(3.14) >= 0)       # 输出: True
性能考虑

对于复杂计算,考虑优化绝对值操作:

class OptimizedVector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self._cached_abs = None
    
    def __abs__(self):
        if self._cached_abs is None:
            self._cached_abs = (self.x**2 + self.y**2)**0.5
        return self._cached_abs

object.__invert__(self)

功能特性

__invert__ 是 Python 中用于实现按位取反运算的特殊方法,它让对象能够响应 ~ 操作符。按位取反运算是一种一元位操作,它将操作数的每个二进制位进行翻转:0变为1,1变为0。按位取反运算在计算机科学中用于位级别的逻辑反转,是二进制运算中的基本操作之一。这种运算的核心原理是对所有位进行逻辑非操作,产生原值的位补码。除了传统的位取反操作,~ 操作符还可以被重载用于实现逻辑反转、状态切换或互补操作等语义。

应用示例

二进制位翻转

实现基本的按位取反操作:

class BitValue:
    def __init__(self, value):
        self.value = value
    
    def __invert__(self):
        # 按位取反操作
        return BitValue(~self.value)
    
    def __repr__(self):
        return f"BitValue({self.value})"

# 使用示例
num = BitValue(0b1010)  # 二进制 1010 (十进制 10)
inverted = ~num
print(f"原值: {num}")      # 输出: BitValue(10)
print(f"取反: {inverted}") # 输出: BitValue(-11)
集合补集操作

重载取反操作符用于集合补集:

class NumberSet:
    def __init__(self, numbers, universe=None):
        self.numbers = set(numbers)
        self.universe = universe
    
    def __invert__(self):
        if self.universe is not None:
            # 计算相对于全集的补集
            return NumberSet(self.universe - self.numbers, self.universe)
        return NotImplemented
    
    def __repr__(self):
        return f"集合: {self.numbers}"

# 使用示例
universe = {1, 2, 3, 4, 5}
subset = NumberSet({1, 3, 5}, universe)
complement = ~subset
print(f"子集: {subset}")      # 输出: 集合: {1, 3, 5}
print(f"补集: {complement}")  # 输出: 集合: {2, 4}

注意事项

整数取反的符号行为

整数取反操作会产生符号变化:

# 正数取反变成负数
print(~5)   # 输出: -6

# 负数取反变成正数  
print(~-5)  # 输出: 4

这是由于二进制补码表示法的特性。

与逻辑非的区别

~not 操作符有重要区别:

# 按位取反(~) - 位级操作
print(~0b1010)  # 输出: -11 (位翻转)

# 逻辑非(not) - 布尔操作
print(not 10)   # 输出: False (真值测试)
浮点数的处理

取反运算通常只适用于整数类型:

# 这些操作会抛出TypeError
# ~3.14
# ~5.0

如果需要对其他类型支持取反语义,需要在 __invert__ 方法中明确定义。

可逆性考虑

取反操作通常是可逆的:

class ReversibleInvert:
    def __init__(self, value):
        self.value = value
    
    def __invert__(self):
        return ReversibleInvert(~self.value)
    
    # 连续两次取反应该恢复原值(在整数范围内)
    def test_reversibility(self):
        return ~~self.value == self.value

object.__complex__(self)

功能特性

__complex__ 是 Python 中用于实现复数转换的特殊方法,它让对象能够响应 complex() 内置函数的调用。这个方法定义了如何将自定义对象转换为 Python 的标准复数类型。复数转换在科学计算、工程分析和信号处理等领域具有重要作用。Python 的复数由实部和虚部组成,表示为 real + imag*j 的形式。__complex__ 方法允许自定义对象定义其到这种标准复数表示的转换逻辑,使得对象能够无缝集成到 Python 的数学生态系统中。

应用示例

自定义复数类型的转换

实现自定义复数类到标准复数的转换:

class CustomComplex:
    def __init__(self, real, imag):
        self.real = real
        self.imag = imag
    
    def __complex__(self):
        # 转换为标准复数类型
        return complex(self.real, self.imag)
    
    def __repr__(self):
        return f"CustomComplex({self.real}, {self.imag})"

# 使用示例
custom = CustomComplex(3, 4)
standard = complex(custom)
print(f"自定义复数: {custom}")  # 输出: CustomComplex(3, 4)
print(f"标准复数: {standard}")  # 输出: (3+4j)

注意事项

返回值类型要求

__complex__ 必须返回 complex 类型:

class StrictComplex:
    def __init__(self, real, imag):
        self.real = real
        self.imag = imag
    
    def __complex__(self):
        # 必须返回complex类型,不能是元组或其他类型
        return complex(self.real, self.imag)  # 正确
        # return (self.real, self.imag)      # 错误!
与其他转换方法的协调

复数转换应该与其他数值转换方法协调:

class CoordinatedConversions:
    def __init__(self, real, imag):
        self.real = real
        self.imag = imag
    
    def __complex__(self):
        return complex(self.real, self.imag)
    
    def __float__(self):
        # 转换为浮点数时通常使用实部
        return float(self.real)
    
    def __int__(self):
        # 转换为整数时通常使用实部
        return int(self.real)
精度和误差考虑

复数转换可能涉及浮点数精度问题:

class PrecisionAware:
    def __complex__(self):
        # 对于需要高精度的场景
        from decimal import Decimal
        real_decimal = Decimal(str(self.real))
        imag_decimal = Decimal(str(self.imag))
        return complex(float(real_decimal), float(imag_decimal))
异常处理

转换过程中应该处理可能的错误:

class RobustComplex:
    def __complex__(self):
        try:
            # 尝试转换,处理可能的数值错误
            return complex(self.validated_real(), self.validated_imag())
        except (TypeError, ValueError) as e:
            raise TypeError(f"无法转换为复数: {e}")

object.__int__(self)

功能特性

__int__ 是 Python 中用于实现整数转换的特殊方法,它让对象能够响应 int() 内置函数的调用。这个方法定义了如何将自定义对象转换为 Python 的标准整数类型。整数转换在编程中极为常见,从数值计算到索引操作,整数都是最基础的数据类型之一。__int__ 方法允许自定义对象定义其到整数的转换逻辑,使得对象能够参与整数运算、用作索引或与其他整数类型进行交互。这种转换机制为对象提供了与 Python 核心数值系统集成的能力。

应用示例

自定义数值类型的整数转换

实现自定义数值类到整数的转换:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __int__(self):
        # 转换为整数类型
        return int(self.value)
    
    def __repr__(self):
        return f"CustomNumber({self.value})"

# 使用示例
custom_num = CustomNumber(3.14)
as_int = int(custom_num)
print(f"自定义数值: {custom_num}")  # 输出: CustomNumber(3.14)
print(f"整数形式: {as_int}")       # 输出: 3

注意事项

返回值类型要求

__int__ 必须返回 int 类型:

class StrictInt:
    def __init__(self, value):
        self.value = value
    
    def __int__(self):
        # 必须返回int类型,不能是float或其他类型
        return int(self.value)  # 正确
        # return self.value    # 可能错误,如果value是float
精度丢失处理

整数转换可能导致精度丢失:

class PrecisionAware:
    def __init__(self, value):
        self.value = value
    
    def __int__(self):
        # 明确处理精度丢失,可以选择四舍五入或截断
        return round(self.value)  # 四舍五入
        # return int(self.value)  # 直接截断
与其他转换方法的协调

整数转换应该与其他数值转换方法保持一致:

class CoordinatedConversions:
    def __init__(self, value):
        self.value = value
    
    def __int__(self):
        return int(self.value)
    
    def __float__(self):
        return float(self.value)
    
    def __bool__(self):
        return bool(self.value)
边界情况和错误处理

处理转换过程中的边界情况:

class RobustInt:
    def __int__(self):
        try:
            # 验证并执行转换
            if self.value > 2**31 - 1 or self.value < -2**31:
                raise OverflowError("数值超出整数范围")
            return int(self.value)
        except (TypeError, ValueError) as e:
            raise TypeError(f"无法转换为整数: {e}")

object.__float__(self)

功能特性

__float__ 是 Python 中用于实现浮点数转换的特殊方法,它让对象能够响应 float() 内置函数的调用。这个方法定义了如何将自定义对象转换为 Python 的标准浮点数类型。浮点数转换在科学计算、工程应用和数据分析中具有基础性作用。与整数转换不同,浮点数转换保留了小数部分,能够表示更精确的数值。__float__ 方法允许自定义对象定义其到浮点数的转换逻辑,使得对象能够参与浮点运算、数学函数计算或与其他数值类型进行精确交互。

应用示例

自定义数值类型的浮点数转换

实现自定义数值类到浮点数的转换:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __float__(self):
        # 转换为浮点数类型
        return float(self.value)
    
    def __repr__(self):
        return f"CustomNumber({self.value})"

# 使用示例
custom_num = CustomNumber(42)
as_float = float(custom_num)
print(f"自定义数值: {custom_num}")  # 输出: CustomNumber(42)
print(f"浮点数形式: {as_float}")   # 输出: 42.0

注意事项

返回值类型要求

__float__ 必须返回 float 类型:

class StrictFloat:
    def __init__(self, value):
        self.value = value
    
    def __float__(self):
        # 必须返回float类型
        return float(self.value)  # 正确
        # return self.value      # 可能错误,如果value是整数
浮点数精度问题

浮点数转换可能涉及精度损失:

class PrecisionAware:
    def __init__(self, value):
        self.value = value
    
    def __float__(self):
        # 对于需要高精度的场景,可以使用decimal模块
        from decimal import Decimal, getcontext
        getcontext().prec = 10  # 设置精度
        return float(Decimal(str(self.value)))
与其他转换方法的协调

浮点数转换应该与其他数值转换方法保持一致:

class CoordinatedConversions:
    def __init__(self, value):
        self.value = value
    
    def __float__(self):
        return float(self.value)
    
    def __int__(self):
        return int(self.value)
    
    def __complex__(self):
        return complex(self.value, 0)
无穷大和NaN的处理

处理特殊的浮点数值:

class SpecialValues:
    def __init__(self, value):
        self.value = value
    
    def __float__(self):
        import math
        if self.value == "inf":
            return float('inf')
        elif self.value == "-inf":
            return float('-inf')
        elif self.value == "nan":
            return float('nan')
        else:
            return float(self.value)

object.__index__(self)

功能特性

__index__ 是 Python 中用于实现索引转换的特殊方法,它让对象能够在使用索引操作的上下文中被转换为整数。这个方法定义了如何将自定义对象转换为适合用作索引的整数值。索引转换在序列操作、切片和位操作中具有基础性作用。与其他转换方法不同,__index__ 专门用于那些需要整数索引的上下文环境,如列表索引、切片操作或位运算。这个方法确保自定义对象能够无缝地集成到 Python 的索引系统中,为序列类和位操作类对象提供统一的接口。

应用示例

自定义索引对象

创建可以用作索引的自定义对象:

class CustomIndex:
    def __init__(self, value):
        self.value = value
    
    def __index__(self):
        # 返回用作索引的整数值
        return self.value
    
    def __repr__(self):
        return f"CustomIndex({self.value})"

# 使用示例
index_obj = CustomIndex(2)
my_list = [10, 20, 30, 40, 50]

# 在切片操作中使用自定义索引对象
result = my_list[index_obj]
print(f"列表: {my_list}")      # 输出: [10, 20, 30, 40, 50]
print(f"索引对象: {index_obj}") # 输出: CustomIndex(2)
print(f"索引结果: {result}")    # 输出: 30
枚举索引

创建基于枚举值的索引:

class EnumIndex:
    def __init__(self, enum_value):
        self.enum_value = enum_value
    
    def __index__(self):
        # 将枚举值转换为索引
        mapping = {'first': 0, 'second': 1, 'third': 2}
        return mapping.get(self.enum_value, -1)
    
    def __repr__(self):
        return f"EnumIndex({self.enum_value})"

# 使用示例
enum_idx = EnumIndex('second')
my_list = ['alpha', 'beta', 'gamma']

result = my_list[enum_idx]
print(f"枚举索引: {enum_idx}") # 输出: EnumIndex(second)
print(f"列表: {my_list}")     # 输出: ['alpha', 'beta', 'gamma']
print(f"访问结果: {result}")   # 输出: beta

注意事项

返回值类型要求

__index__ 必须返回 int 类型:

class StrictIndex:
    def __init__(self, value):
        self.value = value
    
    def __index__(self):
        # 必须返回int类型
        return int(self.value)  # 正确
        # return self.value     # 可能错误,如果value不是整数
索引范围验证

确保索引值在有效范围内:

class SafeIndex:
    def __init__(self, value, max_index):
        self.value = value
        self.max_index = max_index
    
    def __index__(self):
        # 确保索引在有效范围内
        if self.value < 0 or self.value >= self.max_index:
            raise IndexError("索引超出范围")
        return self.value
与其他转换方法的区别

__index____int__ 有不同的使用场景:

class DifferentiatedConversions:
    def __init__(self, value):
        self.value = value
    
    def __index__(self):
        # 用于索引上下文
        return self.value
    
    def __int__(self):
        # 用于一般的整数转换
        return self.value
    
    # __index__ 在切片和序列索引中使用
    # __int__ 在数学运算和类型转换中使用
性能考虑

对于频繁使用的索引,考虑性能优化:

class CachedIndex:
    def __init__(self, value):
        self.value = value
        self._cached_index = None
    
    def __index__(self):
        if self._cached_index is None:
            # 计算索引值并缓存
            self._cached_index = self.calculate_index()
        return self._cached_index

object.__round__(self[, ndigits])

功能特性

__round__ 是 Python 中用于实现四舍五入运算的特殊方法,它让对象能够响应 round() 内置函数的调用。这个方法定义了如何对自定义对象进行四舍五入操作,可以指定保留的小数位数。四舍五入运算在数值处理、金融计算和数据显示中具有重要作用。与简单的类型转换不同,四舍五入涉及数值的近似处理,需要在精度和简洁性之间找到平衡。__round__ 方法允许自定义对象定义其四舍五入的逻辑,使得对象能够按照特定规则进行数值近似。

应用示例

自定义数值类型的四舍五入

实现自定义数值类的四舍五入:

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __round__(self, ndigits=None):
        # 四舍五入到指定小数位
        if ndigits is None:
            return round(self.value)
        return round(self.value, ndigits)
    
    def __repr__(self):
        return f"CustomNumber({self.value})"

# 使用示例
num = CustomNumber(3.14159)
rounded1 = round(num)      # 四舍五入到整数
rounded2 = round(num, 2)   # 四舍五入到2位小数

print(f"原值: {num}")        # 输出: CustomNumber(3.14159)
print(f"取整: {rounded1}")   # 输出: 3
print(f"两位: {rounded2}")   # 输出: 3.14

注意事项

ndigits 参数处理

正确处理可选的 ndigits 参数:

class RobustRound:
    def __init__(self, value):
        self.value = value
    
    def __round__(self, ndigits=None):
        # 处理各种ndigits情况
        if ndigits is None:
            return round(self.value)
        elif isinstance(ndigits, int):
            return round(self.value, ndigits)
        else:
            raise TypeError("ndigits必须是整数或None")
四舍五入规则

明确四舍五入的规则:

class BankersRounding:
    def __init__(self, value):
        self.value = value
    
    def __round__(self, ndigits=None):
        # 银行家舍入法:四舍六入五成双
        from decimal import Decimal, ROUND_HALF_EVEN
        decimal_value = Decimal(str(self.value))
        if ndigits is None:
            rounded = decimal_value.quantize(Decimal('1'), rounding=ROUND_HALF_EVEN)
        else:
            rounded = decimal_value.quantize(Decimal('1.' + '0'*ndigits), rounding=ROUND_HALF_EVEN)
        return float(rounded)
边界情况处理

处理边界情况和异常:

class SafeRound:
    def __init__(self, value):
        self.value = value
    
    def __round__(self, ndigits=None):
        try:
            # 处理可能的数值错误
            if ndigits is not None and ndigits < 0:
                raise ValueError("ndigits不能为负数")
            return round(self.value, ndigits)
        except (TypeError, ValueError) as e:
            raise TypeError(f"四舍五入失败: {e}")

object.__trunc__(self)

功能特性

__trunc__ 是 Python 中用于实现截断取整运算的特殊方法,它让对象能够响应 math.trunc() 函数的调用。这个方法定义了如何将自定义对象截断为整数,即向零方向取整。截断取整在数学计算和数据处理中具有特定作用,它与四舍五入和其他取整方式有着本质区别。截断操作总是向零方向取整,正数向下取整,负数向上取整。这种取整方式在金融计算、科学模拟和数值分析中特别有用,因为它避免了四舍五入可能引入的系统性偏差。

应用示例

自定义数值类型的截断取整

实现自定义数值类的截断取整:

import math

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __trunc__(self):
        # 向零方向截断取整
        return math.trunc(self.value)
    
    def __repr__(self):
        return f"CustomNumber({self.value})"

# 使用示例
num1 = CustomNumber(3.7)
num2 = CustomNumber(-2.9)

trunc1 = math.trunc(num1)
trunc2 = math.trunc(num2)

print(f"原值1: {num1}")    # 输出: CustomNumber(3.7)
print(f"截断1: {trunc1}")  # 输出: 3
print(f"原值2: {num2}")    # 输出: CustomNumber(-2.9)
print(f"截断2: {trunc2}")  # 输出: -

注意事项

__int__ 方法的区别

截断取整与普通整数转换的区别:

import math

class DifferentiatedTrunc:
    def __init__(self, value):
        self.value = value
    
    def __trunc__(self):
        # 明确向零方向截断
        return math.trunc(self.value)
    
    def __int__(self):
        # 普通整数转换(在Python中与trunc行为相同)
        return int(self.value)
    
    # 对于正数:trunc(3.7)=3, int(3.7)=3
    # 对于负数:trunc(-3.7)=-3, int(-3.7)=-3
返回值类型要求

__trunc__ 必须返回 int 类型:

import math

class StrictTrunc:
    def __init__(self, value):
        self.value = value
    
    def __trunc__(self):
        # 必须返回int类型
        return math.trunc(self.value)  # 正确
        # return self.value           # 错误,如果value是浮点数
边界情况处理

处理特殊值的截断:

import math

class RobustTrunc:
    def __init__(self, value):
        self.value = value
    
    def __trunc__(self):
        # 处理无穷大和NaN等特殊情况
        if math.isinf(self.value):
            raise ValueError("无穷大不能截断")
        elif math.isnan(self.value):
            raise ValueError("NaN不能截断")
        return math.trunc(self.value)
与数学函数的协调

确保截断操作与其他数学操作一致:

import math

class MathematicalObject:
    def __trunc__(self):
        # 截断应该与浮点转换协调
        float_version = float(self)
        return math.trunc(float_version)
    
    def __float__(self):
        # 定义到浮点数的转换
        return self.to_float()

object.__floor__(self)

功能特性

__floor__ 是 Python 中用于实现向下取整运算的特殊方法,它让对象能够响应 math.floor() 函数的调用。这个方法定义了如何将自定义对象向下取整到最接近的整数。向下取整在数学计算和数据处理中具有重要作用,它总是将数值向负无穷方向取整。与截断取整不同,向下取整对于负数的处理方式不同:负数会变得更小。这种取整方式在数学证明、算法设计和数值计算中特别有用,因为它提供了可预测的取整行为。

应用示例

自定义数值类型的向下取整

实现自定义数值类的向下取整:

import math

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __floor__(self):
        # 向下取整到最接近的整数
        return math.floor(self.value)
    
    def __repr__(self):
        return f"CustomNumber({self.value})"

# 使用示例
num1 = CustomNumber(3.7)
num2 = CustomNumber(-2.3)

floor1 = math.floor(num1)
floor2 = math.floor(num2)

print(f"原值1: {num1}")    # 输出: CustomNumber(3.7)
print(f"向下取整1: {floor1}")  # 输出: 3
print(f"原值2: {num2}")    # 输出: CustomNumber(-2.3)
print(f"向下取整2: {floor2}")  # 输出: -3

注意事项

__ceil__ 方法的对应关系

向下取整与向上取整形成对应:

import math

class FloorCeilPair:
    def __init__(self, value):
        self.value = value
    
    def __floor__(self):
        # 向下取整
        return math.floor(self.value)
    
    def __ceil__(self):
        # 向上取整
        return math.ceil(self.value)
返回值类型要求

__floor__ 必须返回 int 类型:

import math

class StrictFloor:
    def __init__(self, value):
        self.value = value
    
    def __floor__(self):
        # 必须返回int类型
        return math.floor(self.value)  # 正确
        # return self.value           # 错误,如果value是浮点数
边界情况处理

处理特殊值的向下取整:

import math

class RobustFloor:
    def __init__(self, value):
        self.value = value
    
    def __floor__(self):
        # 处理无穷大和NaN等特殊情况
        if math.isinf(self.value):
            if self.value > 0:
                return math.inf
            else:
                return -math.inf
        elif math.isnan(self.value):
            raise ValueError("NaN不能向下取整")
        return math.floor(self.value)
与数学函数的协调

确保向下取整与其他数学操作一致:

import math

class MathematicalObject:
    def __floor__(self):
        # 向下取整应该与浮点转换协调
        float_version = float(self)
        return math.floor(float_version)
    
    def __float__(self):
        # 定义到浮点数的转换
        return self.to_float()

object.__ceil__(self)

功能特性

__ceil__ 是 Python 中用于实现向上取整运算的特殊方法,它让对象能够响应 math.ceil() 函数的调用。这个方法定义了如何将自定义对象向上取整到最接近的整数。

向上取整在数学计算和实际应用中具有重要作用,它总是将数值向正无穷方向取整。与向下取整相反,向上取整确保结果不小于原值。这种取整方式在资源分配、容量规划和进度计算中特别有用,因为它提供了保守的资源估计。

核心特性:

  • 响应 math.ceil() 函数:math.ceil(obj) 调用 obj.__ceil__()
  • 必须返回一个整数,表示向上取整的结果
  • 向上取整总是向正无穷方向取整
  • 是 Python 数值处理体系的重要组成部分
  • 在资源规划、容量计算和进度估算中特别重要

应用示例

自定义数值类型的向上取整

实现自定义数值类的向上取整:

import math

class CustomNumber:
    def __init__(self, value):
        self.value = value
    
    def __ceil__(self):
        # 向上取整到最接近的整数
        return math.ceil(self.value)
    
    def __repr__(self):
        return f"CustomNumber({self.value})"

# 使用示例
num1 = CustomNumber(3.2)
num2 = CustomNumber(-2.7)

ceil1 = math.ceil(num1)
ceil2 = math.ceil(num2)

print(f"原值1: {num1}")    # 输出: CustomNumber(3.2)
print(f"向上取整1: {ceil1}")  # 输出: 4
print(f"原值2: {num2}")    # 输出: CustomNumber(-2.7)
print(f"向上取整2: {ceil2}")  # 输出: -2

注意事项

__floor__ 方法的对应关系

向上取整与向下取整形成对应:

import math

class CeilFloorPair:
    def __init__(self, value):
        self.value = value
    
    def __ceil__(self):
        # 向上取整
        return math.ceil(self.value)
    
    def __floor__(self):
        # 向下取整
        return math.floor(self.value)
返回值类型要求

__ceil__ 必须返回 int 类型:

import math

class StrictCeil:
    def __init__(self, value):
        self.value = value
    
    def __ceil__(self):
        # 必须返回int类型
        return math.ceil(self.value)  # 正确
        # return self.value          # 错误,如果value是浮点数
边界情况处理

处理特殊值的向上取整:

import math

class RobustCeil:
    def __init__(self, value):
        self.value = value
    
    def __ceil__(self):
        # 处理无穷大和NaN等特殊情况
        if math.isinf(self.value):
            if self.value > 0:
                return math.inf
            else:
                return -math.inf
        elif math.isnan(self.value):
            raise ValueError("NaN不能向上取整")
        return math.ceil(self.value)
性能考虑

对于频繁的向上取整操作,考虑性能优化:

import math

class CachedCeil:
    def __init__(self, value):
        self.value = value
        self._cached_ceil = None
    
    def __ceil__(self):
        if self._cached_ceil is None:
            self._cached_ceil = math.ceil(self.value)
        return self._cached_ceil

小结

方法 功能 应用
object.__add__(self, other) 实现加法运算 + 数值相加、字符串/列表拼接、自定义对象的合并操作
object.__sub__(self, other) 实现减法运算 - 数值相减、集合差集、自定义对象的移除操作
object.__mul__(self, other) 实现乘法运算 * 数值相乘、字符串/列表重复 ("a" * 3)
object.__matmul__(self, other) 实现矩阵乘法运算 @ 主要用于NumPy等科学计算库中的矩阵乘法
object.__truediv__(self, other) 实现真除法 / (返回浮点数) 数值除法
object.__floordiv__(self, other) 实现整数除法 // (向下取整) 数值除法(取整)
object.__mod__(self, other) 实现取模运算 % 数值取余、字符串格式化 (旧式)
object.__divmod__(self, other) 实现内置函数 divmod(),返回一个包含商和余数的元组 (//, %) 同时获取商和余数
object.__pow__(self, other) 实现幂运算 **pow() 函数 计算乘方
object.__lshift__(self, other) 实现按位左移运算 << 整数位操作
object.__rshift__(self, other) 实现按位右移运算 >> 整数位操作
object.__and__(self, other) 实现按位与运算 & 整数位操作、集合交集
object.__xor__(self, other) 实现按位异或运算 ^ 整数位操作、集合对称差集
object.__or__(self, other) 实现按位或运算 | 整数位操作、集合并集
object.__neg__(self) 实现一元负号运算 -self 取数值的相反数
object.__pos__(self) 实现一元正号运算 +self 通常返回对象本身,用于对称性
object.__abs__(self) 实现内置函数 abs(),返回绝对值 计算数值的绝对值
object.__invert__(self) 实现按位取反运算 ~ 整数位操作
object.__complex__(self) 实现类型转换,被 complex() 函数调用时调用 将对象转换为复数类型
object.__int__(self) 实现类型转换,被 int() 函数调用时调用 将对象转换为整数类型
object.__float__(self) 实现类型转换,被 float() 函数调用时调用 将对象转换为浮点数类型
object.__index__(self) 在需要无损地转换为整数时被调用(如切片、bin(), hex(), oct() 用于自定义的整数类型,使其能用于切片索引或进制转换
object.__round__(self[, ndigits]) 实现内置函数 round() 的行为 对对象进行四舍五入,可指定小数位数 ndigits
object.__trunc__(self) 实现 math.trunc() 函数的行为,截取为整数 向零截断,返回整数
object.__floor__(self) 实现 math.floor() 函数的行为,向下取整 返回不大于原数的最大整数
object.__ceil__(self) 实现 math.ceil() 函数的行为,向上取整 返回不小于原数的最小整数

:一些方法具有以下规律

  • 反射交换作数——方法前加r
  • 增广算术赋值——方法前加i

上下文管理器

上下文管理器的核心就是 __enter____exit__ 这两个特殊方法,它们使得资源管理更加安全和简洁

  • object.__enter__(self)

    • 作用:进入上下文时调用,返回的对象会被 as 子句接收
    • 调用时机:执行 with 语句时
    • 返回值:通常返回 self 或其他需要管理的资源
  • object.__exit__(self, exc_type, exc_val, traceback)

    • 作用:退出上下文时调用,处理清理工作和异常

      • 参数:
        • exc_type:异常类型(无异常时为 None)
        • exc_val:异常实例
        • traceback:异常跟踪信息
    • 返回值:返回 True 表示异常已处理,False 或 None 表示异常继续传播

应用示例

文件操作上下文管理

实现文件操作的自动打开和关闭:

class ManagedFile:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
    
    def __enter__(self):
        # 进入上下文时打开文件
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 退出上下文时关闭文件
        self.file.close()

# 使用示例
with ManagedFile('data.txt', 'w') as f:
    f.write('Hello, World!')
# 文件会自动关闭,无需手动调用close()
数据库连接管理

管理数据库连接的自动连接和断开:

class DatabaseConnection:
    def __init__(self, connection_string):
        self.connection_string = connection_string
    
    def __enter__(self):
        # 进入上下文时建立连接
        self.connection = connect(self.connection_string)
        return self.connection
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 退出上下文时关闭连接
        self.connection.close()

# 使用示例
with DatabaseConnection('db://localhost/mydb') as db:
    result = db.execute('SELECT * FROM users')
计时器上下文

实现代码执行时间的测量:

import time

class Timer:
    def __enter__(self):
        # 进入上下文时记录开始时间
        self.start = time.time()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 退出上下文时计算并输出执行时间
        self.end = time.time()
        print(f'执行时间: {self.end - self.start:.2f}秒')

# 使用示例
with Timer():
    # 模拟耗时操作
    time.sleep(1)
# 自动输出: 执行时间: 1.00秒

注意事项

返回值的使用

__enter__ 的返回值会被赋值给 as 后面的变量:

class ContextExample:
    def __enter__(self):
        # 返回的值可用于with块内部
        return "上下文对象"
    
    def __exit__(self, *args):
        pass

with ContextExample() as obj:
    print(obj)  # 输出: "上下文对象"
异常处理

__enter__ 中的异常会阻止 __exit__ 的执行:

class RobustContext:
    def __enter__(self):
        try:
            # 资源分配可能失败
            self.resource = allocate_resource()
            return self.resource
        except Exception as e:
            # 处理分配失败的情况
            raise RuntimeError(f"资源分配失败: {e}")
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 如果__enter__失败,这个方法不会被调用
        if self.resource:
            self.resource.release()
__exit__ 的协调

确保 __enter____exit__ 的正确配合:

class CoordinatedContext:
    def __enter__(self):
        self.setup_done = False
        try:
            self.setup_resources()
            self.setup_done = True
            return self
        except:
            self.cleanup()  # 设置失败时立即清理
            raise
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.setup_done:
            self.cleanup()
资源泄漏防护

确保资源在任何情况下都能被释放:

class LeakProofContext:
    def __enter__(self):
        self.resource = None
        try:
            self.resource = acquire_expensive_resource()
            return self.resource
        except:
            # 即使获取失败也要清理已分配的资源
            if self.resource:
                self.resource.cleanup()
            raise
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.resource:
            self.resource.cleanup()
方法 功能 应用
object.__enter__(self) 进入上下文时调用,返回要管理的资源对象 资源初始化、设置环境状态、返回被管理的对象
object.__exit__(self, exc_type, exc_value, traceback) 退出上下文时调用,负责清理资源和异常处理 资源释放、状态恢复、异常处理和日志记录

在类模式匹配中自定义位置参数

object.__match_args__

功能特性

__match_args__Python 3.10 中引入的类属性,用于支持结构模式匹配(Structural Pattern Matching)功能。它不是方法,而是一个类级别的元组属性,定义了在模式匹配中如何按位置匹配类的属性。结构模式匹配是 Python 现代版本中的重要特性,允许对复杂数据结构进行声明式匹配。__match_args__ 属性为自定义类提供了与内置类型相同的模式匹配能力,使得在 match 语句中可以按位置访问对象的属性,而不是只能按属性名访问。

应用示例

基本的数据类模式匹配

为简单数据类启用模式匹配:

class Point:
    __match_args__ = ('x', 'y')
    
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 使用示例
def describe_point(point):
    match point:
        case Point(0, 0):
            return "原点"
        case Point(x, 0):
            return f"X轴上的点,x={x}"
        case Point(0, y):
            return f"Y轴上的点,y={y}"
        case Point(x, y):
            return f"普通点 ({x}, {y})"

p = Point(3, 4)
print(describe_point(p))  # 输出: 普通点 (3, 4)

注意事项

属性名称必须正确

__match_args__ 中的属性名必须与实际属性对应:

class CorrectMatchArgs:
    __match_args__ = ('correct_attr',)  # 必须与实际属性名匹配
    
    def __init__(self, correct_attr):
        self.correct_attr = correct_attr  # 这个名称必须匹配

# 如果属性名不匹配,模式匹配会失败
class IncorrectMatchArgs:
    __match_args__ = ('x',)  # 声明使用属性x
    
    def __init__(self, y):
        self.y = y  # 但实际属性是y,这会导致错误
__init__ 参数的协调

通常 __match_args____init__ 参数对应:

class CoordinatedClass:
    __match_args__ = ('name', 'age')  # 通常与__init__参数顺序一致
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
继承考虑

在继承体系中正确处理 __match_args__

class Base:
    __match_args__ = ('base_attr',)
    
    def __init__(self, base_attr):
        self.base_attr = base_attr

class Derived(Base):
    # 如果需要扩展模式匹配,可以重新定义__match_args__
    __match_args__ = ('base_attr', 'derived_attr')
    
    def __init__(self, base_attr, derived_attr):
        super().__init__(base_attr)
        self.derived_attr = derived_attr
可选属性的处理

对于可选属性,需要谨慎设计:

class OptionalAttributes:
    __match_args__ = ('required', 'optional')
    
    def __init__(self, required, optional=None):
        self.required = required
        self.optional = optional

# 使用示例
def handle_optional(obj):
    match obj:
        case OptionalAttributes(req, None):
            return f"只有必需属性: {req}"
        case OptionalAttributes(req, opt):
            return f"两个属性: {req}, {opt}"

__match_args__ 类属性为 Python 自定义类提供了结构模式匹配能力,使得在 match 语句中可以直观地解构和匹配复杂对象。正确的实现需要确保属性名称的正确性、与构造函数的协调性以及在继承体系中的一致性,确保模式匹配能够准确可靠地工作。

方法 功能 应用
object.__match_args__ 支持结构模式匹配的位置参数 数据验证、状态机、复杂条件分支

模拟缓冲区类型

object.__buffer__(self, flags)

功能特性

__buffer__方法是Python中用于实现缓冲区协议的特殊方法,允许对象以底层内存缓冲区的方式暴露其内部数据。这种方法提供了对对象内部内存的直接访问,避免了数据复制,从而显著提高了数据处理的效率。

主要功能特性包括:

  • 内存高效访问:直接暴露对象内部的内存布局,避免不必要的数据拷贝
  • 支持缓冲区协议:使得对象可以与需要缓冲区接口的C扩展模块和内置函数无缝协作
  • 零拷贝操作:允许多个消费者同时访问相同数据而无需复制
  • 灵活的内存视图:通过memoryview对象提供对底层缓冲区的安全访问

应用示例

数值数组的高效处理

import array

class EfficientArray:
    def __init__(self, data):
        self._data = array.array('i', data)  # 整数数组
    
    def __buffer__(self, flags):
        # 返回底层数组的缓冲区
        return self._data.__buffer__(flags)
    
    def __len__(self):
        return len(self._data)

# 创建自定义数组对象
arr = EfficientArray([1, 2, 3, 4, 5])

# 通过memoryview直接访问底层缓冲区
buffer_view = memoryview(arr)
print(buffer_view.tolist())  # 输出: [1, 2, 3, 4, 5]

# 修改缓冲区内容会直接影响原对象
buffer_view[0] = 99
print(arr._data[0])  # 输出: 99

注意事项

实现__buffer__方法时需要特别注意内存安全问题。由于该方法直接暴露对象的内部内存,必须确保:

  • 在缓冲区被使用时,对象不能被意外修改或销毁
  • 需要正确处理flags参数,根据不同的访问标志提供适当的缓冲区
  • 考虑线程安全性,特别是在多线程环境中使用缓冲区时
  • 避免在缓冲区活跃时改变对象的内存布局或大小

此外,__buffer__方法通常与__release_buffer__方法配合使用,用于在缓冲区不再需要时执行必要的清理操作。

方法 功能 应用
object.__buffer__(self, flags) 实现缓冲区协议,暴露内部数据 图像处理、科学计算、文件映射
object.__release_buffer__(self, buffer) 释放缓冲区资源,清理操作 资源管理、内存映射文件、线程安全

注释

函数、类和模块可能包含注释 ,这是一种将信息(通常是类型提示 )与符号关联的方式。

object.__annotations__

功能特性

该属性包含对象的注释。这个方法是懒惰的,因此访问该属性时可能会执行任意代码并引发异常。如果执行成功,属性将被设置为从变量名映射到注释的字典。

该方法用于存储类型注解的特殊字典属性,它会在类或函数被定义时自动创建。这个属性以字典形式保存着参数和返回值的类型提示信息,键为参数名(字符串),值为对应的类型对象。

类型注解本身不会影响程序的运行时行为,但可以通过 __annotations__ 属性在运行时被访问和利用,为类型检查、文档生成、序列化等工具提供元数据支持。

在3.14版本中被更改:注释现在被懒惰地评估

应用示例

获取函数的类型注解
def greet(name: str, age: int) -> str:
    return f"{name} is {age} years old."

# 访问函数的 __annotations__ 属性
print(greet.__annotations__)
# 输出: {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}

定义函数时使用了类型注解,Python 会自动将这些注解收集到函数的 __annotations__ 字典中,其中键包括参数名和特殊的 'return' 键(对应返回值注解)。

获取类的属性类型注解
class Person:
    name: str
    age: int = 18

    def __init__(self, name: str) -> None:
        self.name = name

# 访问类的 __annotations__ 属性
print(Person.__annotations__)
# 输出: {'name': <class 'str'>, 'age': <class 'int'>}

在类定义中直接对类属性进行类型注解(变量注解),这些注解会被收集到类的 __annotations__ 字典中。注意,实例方法的注解不在类的 __annotations__ 中,而在方法自身的 __annotations__ 属性里。

利用注解实现简单验证
class Validated:
    # 类型注解作为元数据存储
    value: int
    
    def __init__(self, value: int) -> None:
        # 利用注解进行简单类型检查
        if not isinstance(value, self.__annotations__['value']):
            raise TypeError(f"Expected {self.__annotations__['value']}, got {type(value)}")
        self.value = value

# 使用验证类
try:
    obj = Validated(42)  # 正常
    print("Validated with 42")
    
    obj2 = Validated("not a number")  # 抛出异常
except TypeError as e:
    print(f"Error: {e}")

通过在 __init__ 方法中访问 self.__annotations__ 来获取期望的类型,并对传入的参数进行类型检查,实现简单的运行时类型验证。

注意事项

  1. __annotations__ 属性是可写的,但通常不建议直接修改它,因为这样会导致类型注解与实际代码不一致。
  2. 如果函数或类没有使用类型注解,__annotations__ 属性可能不存在(函数)或为空字典(类)。使用前应通过 hasattr()getattr() 进行安全访问。
  3. 类型注解在 Python 3.10 及以后版本中,可以通过 inspect.get_annotations() 函数更安全地获取,该函数能正确处理前向引用等复杂情况。
  4. 类型注解的主要用途是静态类型检查和代码文档,虽然可以通过 __annotations__ 在运行时访问,但不应依赖它来实现核心业务逻辑。

object.__annotate__(format)

添加于3.14版本

功能特性

一个注释函数 。返回一个新的词典对象,将属性/参数名映射到它们的注释值。

采用一个格式参数,指定注释值应以何种形式提供。该参数必须是枚举的成员 annotationlib.Format,或与枚举成员对应的整数。

如果注释函数不支持所请求的格式,则必须抛出NotImplementedError错误。注释函数必须始终支持VALUE格式,当使用此格式时不能抛出NotImplementedError()

当以VALUE格式调用时,注释函数可能会抛出NameError,当请求其他格式时,不得引发NameError

如果对象没有任何注释,__annotate__最好设置为None(它无法删除),而不是设置返回空字典的函数。

小结

方法 功能 应用
object.__annotations__ 存储对象的类型注解字典,采用懒加载方式(访问时可能执行代码并引发异常)。 获取函数或类的类型注解(如参数类型、返回值类型),用于运行时反射、类型检查、文档生成等。
object.__annotate__(format) 注释函数,根据指定的 Format 枚举(如 VALUESTRING 等)返回注解字典,支持不同格式的注解。 在需要以特定格式(如值形式、字符串形式)获取注解时使用,提供更灵活、安全的注解访问方式,避免直接操作 __annotations__ 可能带来的副作用。

协程

object.__await__(self)

添加于3.5版本

可等待对象是指一个可在await表达式中使用的对象。可以是coroutine或是具有__await__()方法的对象。从async def函数返回的协程对象是可等待的。

从带有types.coroutine()装饰的生成器返回的生成器迭代对象也是可等待的,但它们不实现__await__()

功能特性

__await__ 是一个用于实现异步可等待对象的特殊方法,它定义了对象在被 await 表达式等待时的行为。通过实现这个方法,可以让自定义的类支持异步等待机制,从而与 Python 的异步编程模型无缝集成。

该方法必须返回一个迭代器,该迭代器将在异步事件循环中被逐步执行。await 表达式实际上是在等待这个迭代器的完成,当迭代器耗尽时,返回的值将成为 await 表达式的结果。

应用示例

自定义异步等待对象
import asyncio

class AsyncCountdown:
    def __init__(self, start):
        self.start = start
    
    def __await__(self):
        # 返回一个生成器迭代器
        for i in range(self.start, 0, -1):
            print(f"倒计时: {i}")
            # 暂停执行,等待下一次迭代
            yield from asyncio.sleep(1).__await__()
        print("发射!")
        return "完成"

async def main():
    countdown = AsyncCountdown(3)
    result = await countdown  # 调用 __await__ 方法
    print(f"结果: {result}")

# 运行异步程序
asyncio.run(main())

代码解释:AsyncCountdown 类通过实现 __await__ 方法成为一个可等待对象。当使用 await countdown 时,Python 会调用 __await__ 方法,返回一个生成器迭代器。该迭代器每秒打印一个倒计时数字,最后返回字符串"完成"。yield from 语句用于将控制权交还给事件循环,模拟异步等待。

包装同步操作为异步接口
import asyncio
from concurrent.futures import ThreadPoolExecutor

class AsyncFileReader:
    def __init__(self, filename):
        self.filename = filename
    
    def __await__(self):
        # 在单独的线程中执行同步IO操作
        loop = asyncio.get_event_loop()
        with ThreadPoolExecutor() as pool:
            # 使用 yield from 将控制权交给事件循环
            data = yield from loop.run_in_executor(
                pool, self._read_file
            ).__await__()
        return data
    
    def _read_file(self):
        # 模拟耗时的同步文件读取
        import time
        time.sleep(2)
        return f"文件 {self.filename} 的内容"

async def main():
    reader = AsyncFileReader("data.txt")
    content = await reader  # 异步等待文件读取完成
    print(f"读取到: {content}")

asyncio.run(main())

代码解释:AsyncFileReader 类将同步的文件读取操作包装成异步接口。__await__ 方法中使用 yield from 将耗时的同步操作委托给线程池执行,同时不阻塞事件循环。这样,同步的IO操作就可以在异步程序中使用,提高了程序的并发性能。

注意事项

  1. __await__ 方法必须返回一个迭代器,通常使用生成器函数或实现迭代器协议的对象。这个迭代器会在异步上下文执行,并通过 yieldyield from 将控制权交还给事件循环。
  2. 当迭代器产生值时,这些值应该也是可等待对象,通常是 asyncio.Future 或其他实现了 __await__ 方法的对象。这确保了异步操作的链式执行。
  3. 如果 __await__ 方法直接返回一个非迭代器对象,会引发 TypeError。必须确保返回值满足迭代器协议。
  4. 在异步编程中,通常更推荐使用 async defawait 语法来定义异步函数,而不是直接实现 __await__ 方法。__await__ 主要用于创建自定义的异步可等待对象,这是相对高级的用法。

异步迭代器

添加于Python 3.5,

3.7版本后有所更改:在Python 3.7之前,__aiter__()可能返回一个可等待对象,该对象会被解析为一个异步迭代器,但在Python 3.7及更高版本中,__aiter__()方法必须返回一个异步迭代器,否则会抛出TypeError错误。

功能特性

__aiter____anext__ 是 Python 异步迭代器协议的核心方法,用于在异步环境中遍历数据。

这对方法使得自定义类能够支持 async for循环,实现在异步程序中按需异步获取元素。

__aiter__ 方法返回异步迭代器对象本身,这与同步迭代器中__iter__ 的作用类似,但用于异步上下文。__anext__ 方法返回一个可等待对象(awaitable),当没有更多元素时引发StopAsyncIteration 异常。这两个方法共同构成了异步迭代器协议,允许在异步循环中逐个产生元素而不阻塞事件循环。

应用示例

异步数据库查询结果集
import asyncio

class AsyncDatabaseCursor:
    def __init__(self, data):
        self.data = data
        self.index = 0
    
    def __aiter__(self):
        # 返回自身作为异步迭代器
        return self
    
    async def __anext__(self):
        if self.index < len(self.data):
            # 模拟异步数据库查询延迟
            await asyncio.sleep(0.1)
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            # 没有更多数据时引发特定异常
            raise StopAsyncIteration

async def process_records():
    # 模拟数据库查询结果
    records = ['记录A', '记录B', '记录C', '记录D']
    cursor = AsyncDatabaseCursor(records)
    
    # 使用 async for 遍历异步迭代器
    async for record in cursor:
        print(f"处理: {record}")

# 运行异步程序
asyncio.run(process_records())

代码解释:AsyncDatabaseCursor 类实现了异步迭代器协议,模拟了从数据库异步获取记录的过程。__aiter__ 方法返回自身,__anext__ 方法在每次调用时模拟异步延迟后返回下一个记录。使用 async for 循环可以异步遍历所有记录,而不会阻塞事件循环。

注意事项

  1. __aiter__ 方法应该返回一个实现了 __anext__ 方法的对象。通常,异步迭代器会返回自身,但也可以返回专门设计的异步迭代器对象。
  2. __anext__ 方法必须返回一个可等待对象(通常是协程),并且在没有更多元素时引发 StopAsyncIteration 异常。这与同步迭代器中的 StopIteration 类似,但是专为异步环境设计。
  3. 异步迭代器只能在使用 async for 循环的异步函数或方法中使用。在同步上下文中使用异步迭代器会导致语法错误。
  4. 异步迭代器通常用于处理需要等待的操作,如网络请求、数据库查询或文件读取。这使得程序在等待IO时不会阻塞整个事件循环。
  5. 从 Python 3.6 开始,还支持异步推导式(async comprehensions),可以与异步迭代器结合使用,提供更简洁的语法来处理异步数据流。

异步上下文管理器

添加于Python 3.5

功能特性

__aenter____aexit__ 是异步上下文管理器协议的核心方法,用于在异步环境中管理资源的获取和释放。这两个方法使得自定义类能够支持 async with 语句,实现在异步程序中对资源进行安全、自动化的管理。

__aenter__ 方法在进入异步上下文时被调用,负责资源的初始化或获取,并可以返回一个值绑定到 as 子句的变量。__aexit__ 方法在退出异步上下文时被调用,无论是否发生异常都会执行,用于资源的清理和释放。与同步上下文管理器不同,这两个方法都是异步的,可以包含 await 表达式,支持异步操作。

应用示例

异步数据库连接管理
import asyncio

class AsyncDatabaseConnection:
    async def __aenter__(self):
        # 模拟异步建立数据库连接
        await asyncio.sleep(0.1)
        print("数据库连接已建立")
        return self  # 返回连接对象
    
    async def __aexit__(self, exc_type, exc_value, traceback):
        # 模拟异步关闭数据库连接
        await asyncio.sleep(0.1)
        print("数据库连接已关闭")
        # 可以在此处处理异常,返回True表示异常已处理
        return False

async def query_database():
    async with AsyncDatabaseConnection() as connection:
        # 在此上下文中执行数据库操作
        print("执行数据库查询操作")
        # 模拟异步查询
        await asyncio.sleep(0.2)

# 运行异步程序
asyncio.run(query_database())

代码解释:AsyncDatabaseConnection 类实现了异步上下文管理器协议,确保数据库连接在使用后自动关闭。__aenter__ 方法模拟异步建立连接并返回连接对象;__aexit__ 方法模拟异步关闭连接。使用 async with 语句可以保证即使发生异常,连接也会被正确关闭。

异步文件读写
import asyncio

class AsyncFileHandler:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
    
    async def __aenter__(self):
        # 模拟异步打开文件
        await asyncio.sleep(0.05)
        print(f"已打开文件 {self.filename}")
        return self
    
    async def read_content(self):
        # 模拟异步读取文件内容
        await asyncio.sleep(0.1)
        return f"这是 {self.filename} 的内容"
    
    async def __aexit__(self, exc_type, exc_value, traceback):
        # 模拟异步关闭文件
        await asyncio.sleep(0.05)
        print(f"已关闭文件 {self.filename}")
        return False  # 不抑制异常

async def process_file():
    async with AsyncFileHandler("data.txt", "r") as file:
        content = await file.read_content()
        print(f"读取到: {content}")

asyncio.run(process_file())

代码解释:AsyncFileHandler 类管理异步文件操作。__aenter__ 方法异步打开文件并返回处理对象;__aexit__ 方法确保文件最终被关闭。在 async with 块中,可以安全地调用异步方法进行文件操作,无需担心资源泄漏。

注意事项

  1. __aenter____aexit__ 方法必须定义为异步方法(使用 async def)。如果将它们定义为同步方法,在使用 async with 时会导致错误。
  2. __aexit__ 方法接收三个异常参数:exc_type(异常类型)、exc_value(异常值)和 traceback(回溯信息)。如果上下文块中未发生异常,这些参数均为 None
  3. __aexit__ 方法返回布尔值。如果返回 True,则表示异常已被处理,不会继续向外传播;如果返回 False(通常情况),异常会正常传播。这允许上下文管理器根据需求决定是否抑制异常。
  4. 异步上下文管理器只能在使用 async with 语句的异步函数或方法中使用。在同步上下文中使用会导致语法错误。
  5. 与同步上下文管理器类似,异步上下文管理器应该确保资源在任何情况下都能被正确释放,即使在发生异常时也是如此。__aexit__ 方法的设计应当保证这一点。
  6. 在 Python 3.11 及以上版本中,可以通过 contextlib.asynccontextmanager 装饰器创建异步上下文管理器,这通常比手动实现 __aenter____aexit__ 更简洁。

小结

方法 功能 应用
object.__await__(self) 返回一个迭代器,使对象成为可等待对象,支持await表达式 实现自定义异步对象的等待逻辑,如异步生成器、异步上下文管理器
object.__aiter__(self) 返回异步迭代器,用于实现异步迭代 定义异步迭代器,如异步文件读取、异步数据库查询等场景
object.__anext__(self) 返回可等待对象,用于异步迭代器的next()操作 在异步迭代器中实现异步获取下一个元素,如逐行读取异步文件
object.__aenter__(self) 异步上下文管理器的进入操作,当使用async with语句时调用 实现异步资源管理,如异步数据库连接、异步网络请求等
object.__aexit__(self, exc_type, exc_value, traceback) 异步上下文管理器的退出操作,当async with语句结束时调用 实现异步资源清理,如关闭异步数据库连接、释放异步网络资源

参考

1

评论区