用 hasattr(obj, '__len__') 最直接安全地判断对象是否支持 len(),它不触发副作用且适用于所有 Python 对象;但无法验证 len 是否合规或无副作用。

用 hasattr 检查 __len__ 方法存在性最直接
判断对象是否支持 len(),本质是看它有没有实现 __len__ 魔术方法。调用 hasattr(obj, '__len__') 是最轻量、最安全的方式——它不触发任何副作用,也不依赖 len() 本身的逻辑。
常见错误是误用 isinstance(obj, collections.abc.Sized):虽然语义正确,但需要导入模块,且在某些自定义类未显式继承或注册时会返回 False(哪怕它有合法的 __len__)。
-
hasattr对所有 Python 对象都有效,包括内置类型、用户类、C 扩展对象 - 注意:有些对象(如生成器)有
__len__但返回TypeError,hasattr仍返回True—— 这属于“支持 len 协议但运行时报错”,需另作处理 - 避免写
hasattr(obj, '__len__') and callable(getattr(obj, '__len__')):多余,__len__不可能是非可调用对象
为什么不能用 try/except + len() 代替
想“先试再用”?try: len(obj) except TypeError: 看似直观,但它实际调用了 len(),违反了“不调用”的前提。更关键的是,某些 __len__ 实现有副作用或高开销:
- 数据库查询封装对象可能把
__len__实现为SELECT COUNT(*) -
懒加载序列在
__len__中触发完整数据加载 - 文件对象的
__len__可能触发seek(0, 2)和tell(),改变文件指针位置
这些场景下,仅做“是否支持”的判断,却意外改变了程序状态或性能表现,很难排查。
getattr + inspect.ismethod 的适用边界
如果需要进一步确认该 __len__ 是实例方法而非类属性或静态值(极少见),可用 inspect.ismethod(getattr(obj, '__len__', None))。但绝大多数情况没必要:
- Python 数据模型规定
__len__必须是可调用对象,且通常为绑定方法 - 手动给实例赋值
obj.__len__ = 42属于非法操作,len()本身也会报TypeError - 真正需要深究的场景极少,比如调试元编程行为或分析第三方库内部机制
注意 __len__ 返回值类型和有效性
hasattr(obj, '__len__') 只说明协议被声明,不保证行为合规。Python 要求 __len__ 必须返回非负整数,否则运行时抛 TypeError:
class BadLen:
def __len__(self):
return -1 # 触发 TypeError: __len__() should return >= 0
len(BadLen()) # 直接报错
如果你在框架中做预检(比如序列化前校验),只检查 __len__ 存在性就够了;但如果后续要依赖其返回值做逻辑分支,就得接受它可能在调用时失败——这是协议的一部分,不是 bug。
真正容易被忽略的是:空容器(如空列表)返回 0,而某些自定义类可能返回 None 或浮点数,这种实现虽罕见,但 hasattr 完全无法捕捉,只能靠文档或测试覆盖。










