Python要求__len__必须返回非负整数,否则调用len()或布尔判断时会直接抛出TypeError: __len__() should return >= 0;常见错误包括减法未校验、返回-1或None、返回非int类型等,均需修正为非负整数。

为什么 __len__ 返回负数会报错
Python 明确要求 __len__ 必须返回非负整数(int),否则在调用 len() 或参与布尔判断(如 if obj:)时直接抛出 TypeError: __len__() should return >= 0。这不是警告,是硬性约束。
常见错误场景包括:误用计数逻辑(比如减法后未校验)、缓存了临时负值、或在异常路径中返回了 -1 表示“无效”。这些都必须修正。
-
__len__内部不能有return -1或类似表达式 - 若底层数据可能为空/未初始化,应返回
0,而非负数或None - 避免在
__len__中抛出其他异常(如KeyError),否则len()调用会中断并暴露底层细节
如何让自定义类支持 len() 和布尔上下文
只要正确定义 __len__,对象就自动获得长度语义和真值行为:空对象(len() == 0)为假,非空为真。不需要额外实现 __bool__ —— 除非你想要不同的真假逻辑(比如“有数据但被禁用”仍算假)。
典型实操写法:
class Stack:
def __init__(self):
self._items = []
<pre class="brush:php;toolbar:false;">def push(self, item):
self._items.append(item)
def __len__(self):
return len(self._items) # 直接委托给内部容器- 返回值必须是
int,不能是float、str或自定义数值类(即使它支持int()) - 尽量复用已知可靠的长度计算(如
len(list)、len(dict)),不要手动遍历计数,除非有特殊需求(如跳过过滤项) - 如果长度计算开销大(如需扫描磁盘文件),要考虑缓存结果,并在数据变更时更新缓存
__len__ 在容器协议中的实际作用边界
__len__ 只影响 len()、bool()、if 判断、以及某些内置函数(如 max() 对空序列的处理)。它**不控制**索引访问(obj[i] 由 __getitem__ 处理)、迭代(由 __iter__ 或 __getitem__ 支持)、或切片(obj[1:3])。
- 即使
__len__返回 5,也不代表对象一定支持obj[4]—— 那是__getitem__的职责 - 若只实现
__len__而没实现__iter__,该对象仍不可用于for x in obj: - 某些类型(如生成器)没有
__len__,这是有意设计,因为长度未知或不可高效获取
调试 __len__ 不生效的三个常见原因
写了 __len__ 却发现 len(obj) 报错或返回意外值,大概率是以下之一:
- 方法名拼错:
__len(少一个下划线)、__lne__、__LEN__—— Python 对名称完全敏感,必须是__len__ - 返回了非
int类型:比如返回numpy.int64或自定义Length类实例,即使它数值上等于 3,也会触发TypeError - 方法被覆盖或屏蔽:父类已有
__len__,而子类未正确继承或重写;或使用了@property包裹导致变成属性而非可调用方法
最简单的验证方式是直接调用 obj.__len__() 看是否可执行且返回非负整数 —— 这比依赖 len() 更早暴露问题。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











