当自定义类未实现__len__方法时,调用len()会抛出TypeError: object of type 'X' has no len();该方法必须存在、返回非负整数,且直接影响if obj:等布尔判断。

为什么 len() 调用报 TypeError: object of type 'X' has no len()
当你自定义一个类(比如 BookShelf 或 Stack),想直接用 len(my_obj) 获取其“大小”,Python 会尝试调用对象的 __len__ 方法。如果没实现,就抛这个错——不是你忘了写,而是 Python 根本找不到入口。
关键点:len() 不是靠属性或普通方法,它只认 __len__ 这个特定名字的魔术方法,且必须返回非负整数。
-
__len__必须返回int,不能是float、None或负数,否则运行时报TypeError: __len__() should return >= 0 - 返回值会被
len()直接使用,不做任何转换;哪怕你返回5.0,也会立刻出错 - 不建议在
__len__里做耗时操作(比如遍历磁盘文件统计行数),因为len()语义上应是 O(1) 的快速查询
__len__ 的典型实现方式和常见陷阱
多数容器类只需返回内部存储结构的长度,但要注意底层数据结构是否真实反映“逻辑长度”。
例如:
class Stack:
def __init__(self):
self._items = []
<pre class="brush:php;toolbar:false;">def push(self, item):
self._items.append(item)
def pop(self):
return self._items.pop()
def __len__(self):
return len(self._items) # ✅ 正确:委托给 list.__len__- 别写
return self._items.__len__()—— 虽然可行,但绕过内置len()的优化,且可读性差 - 如果类用字典模拟稀疏数组(如键为索引、值为元素),
len(self._data)返回的是“已存项数”,而非最大索引+1,这通常是合理行为 - 若内部用固定数组 + 游标(如
self._array和self._size),务必返回self._size,而不是len(self._array),否则会暴露底层容量
和 __bool__ 的隐式联动关系
Python 在判断对象真假值(如 if obj:)时,若没定义 __bool__,会 fallback 到 len(obj) == 0。也就是说,只要实现了 __len__,你就自动获得了“空即 False”的默认布尔行为。
- 这意味着:如果你的类逻辑上“非空”但
__len__返回 0(比如一个等待初始化的缓冲区),它会在if中被当作False,可能引发意料外的分支跳转 - 若需自定义真值逻辑(例如“只要配置加载完成就算 True,不管当前数据多少”),必须显式实现
__bool__,否则__len__仍会被 fallback 使用 - 没有
__len__却有__bool__是完全合法的;但两者都缺,对象默认为True
测试 __len__ 是否生效的最快验证法
别等集成到业务逻辑才发现没起作用,用三行交互式验证最可靠:
>>> s = Stack() >>> len(s) 0 >>> s.push(1); s.push(2) >>> len(s) 2
- 确保测试覆盖边界:空实例、单元素、多元素,以及调用后状态变化是否影响长度(比如
pop()后len()是否减 1) - 不要只测
print(len(s)),而要用assert len(s) == N写进单元测试,避免视觉误判 - 如果类支持切片(
__getitem__),注意len()返回值应与len(list(obj))一致,否则用户用for x in obj:时迭代次数可能和预期不符
真正容易被忽略的是:很多开发者把 __len__ 当作“锦上添花”,直到某天调试 if my_container: 行为异常,才回头翻文档——其实它和 __iter__、__getitem__ 一样,是让类融入 Python 容器协议的关键一环,不是可选装饰。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











