当前位置:首页>python>Python 数据模型本质是一套 API

Python 数据模型本质是一套 API

  • 2026-10-11 08:12:01
Python 数据模型本质是一套 API

大多数 Python 开发者只知道__len__ 对应 len()、__add__ 对应 +。事实上Python 数据模型,有一套完整、规范、官方定义的协议。它让我们自定义的对象,可以融入 Python 语言本身,真值判断、哈希、迭代、上下文管理、属性访问、内存布局…… 全部由它控制。要想用好它,必须理解:CPython 到底在何时、以何种顺序、为何调用这些方法。

双下划线方法是由Python 解释器调用的,而不是我们的业务代码直接调用。当我们写 len(obj),解释器实际执行 type(obj).__len__(obj)。它不会直接调用 obj.__len__(),而是通过类去查找,不是通过实例。

举个小例子
class MyClass:    passobj = MyClass()obj.__len__ = lambda: 42  # 没用!len(obj)  # 直接报错
例子中,双下划线方法完全无效。
真值、相等、哈希:三者是强绑定关系,这是最容易写出隐蔽 Bug 的地方。在真值判断时,__bool__优先,没有__bool__,就用__len__ 长度为0即为假,都没有则为真。
class Container:    def __len__(self):        return len(self.items)c = Container([])if not c:    print("empty")  # 会执行
接下来我们看看相等和哈希,__eq__定义值相等,相等的对象,必须拥有相同的哈希值,一旦定义了__eq__,python会自动把__hash__设定为None,对象变成了不可哈希。
classPoint:    def __eq__(self, other):        ...p = Point(){p}  # 报错:不可哈希
想变成可哈希的值类型,必须同时定义:
def __hash__(self):    return hash((self.x, self.y))
比较运算符和反射机制:
当我们些a<b时,它会先尝试type(a).__lt__(a,b),如果返回NotImplemented python会自动尝试反射操作,type(b).__gt__(b,a) 这就是不同类型可以互相比较的底层原理。
一套完整的运算符协议分三层,正向、反射、原地:
1.正向: a+b -> __add__
2.反射: 3+v (对象在右边) -> __radd__
3.原地: a+=b -> __iadd__
class Vector:    def __add__(self, other): ...    def __radd__(self, other): return self.__add__(other)    def __iadd__(self, other):  # 原地修改        self.x += other.x        return self  # 必须 return self
__radd__让自定义类型能和内置类型无缝配合。
属性访问,比想象中的更强大:
1. __getattribute__ 所有属性访问都会触发,几乎不需要重写
2.__getattr__ 只有查找失败时才会触发,适合懒加载、代理、动态属性
class LazyLoader:    def __getattr__(self, name):        # 找不到时才执行
3. __setattr__ 所有赋值都会触发,必须用super()赋值,否则无限递归
def __setattr__(self, name, value):    super().__setattr__(name, value)
另外,如果想实现一个标准序列 / 容器,只需要实现__len__和__getitem__,我们看个例子:
class Fibonacci:    def __len__(self): ...    def __getitem__(self, index): ...fib[10]       # 可用fib[2:5]      # 可用10 in fib     # 可用for n in fib: # 可用
映射类型(dict 风格)继承 MutableMapping,实现 5 个方法,就能获得 keys()/values()/items() 等全部功能。
__slots__是数据模型的一部分,它的作用是禁止实例创建__dict__,使用固定内存布局,内存占用大幅降低
class Point:    __slots__ = ("x", "y")
需要注意的是,子类必须也定义slot,否则会重写生成dict,优化失效。
最后,希望这篇内容能帮我们更深入的了解python的数据模型。

最新文章

随机文章