如何判断一个对象是否支持 len 但不调用 len()

用 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__ 但返回 TypeErrorhasattr 仍返回 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 完全无法捕捉,只能靠文档或测试覆盖。