实现了 __contains__ 仍报 TypeError 是因方法名拼错、缺少 self 参数、返回非布尔值或未处理所有分支;Python 的 in 操作符优先调用 __contains__,仅当未实现时才退化为迭代。

为什么实现了 __contains__ 还是报 TypeError: argument of type 'X' is not iterable
这个错误说明 Python 没有调用你的 __contains__,而是试图先对对象做迭代(比如调用 __iter__),再逐个比对。Python 的 in 操作符有明确的优先级:它**只在对象没实现 __contains__ 时,才退化为遍历 + 比较**;但如果你的类同时没实现 __iter__,又没正确返回布尔值,或返回了非布尔类型,就可能触发这个错误。常见原因包括:
-
__contains__方法名拼错(比如写成__contain__或__contains) - 方法存在但没加
self参数,导致被当成静态方法调用失败 - 返回值不是
True/False,比如返回整数1或字符串"yes",某些旧版本 Python 会拒绝 - 类继承自某个内置容器(如
list)但没显式覆盖__contains__,实际走的是父类逻辑,而你误以为自己控制了行为
__contains__ 的参数和返回值必须严格匹配
该方法接收两个参数:self 和 item,必须返回一个布尔值(bool 类型)。不要试图返回 None、数字或自定义真值对象——Python 不会帮你转换。例如:
class Bag:
def __init__(self, items):
self._items = list(items)
<pre class="brush:php;toolbar:false;">def __contains__(self, item):
# ✅ 正确:显式返回 bool
return item in self._itemsb = Bag([1, 2, 3]) print(2 in b) # True print("x" in b) # False
如果把最后一行改成 return self._items.count(item) > 0,虽然结果一样,但可读性差;更危险的是写成 return item in self._items or None,这会让返回值变成 None,导致后续逻辑出错。
当底层数据结构不支持直接查找时,别硬套 __contains__
比如你封装了一个链表类,内部只有头指针和 next 链接,没有缓存索引或哈希表。这时 __contains__ 必须遍历,时间复杂度是 O(n)。这不是 bug,是设计使然。但要注意:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 不要在
__contains__里做副作用操作(比如自动添加缺失项、打印日志),因为用户写x in obj时默认这是个纯查询 - 如果查找很慢,且调用频繁,考虑是否该暴露一个
has_item()方法并加文档说明性能特征,而不是只依赖in - 避免在
__contains__中递归调用自身,容易栈溢出;也不要意外触发其他魔术方法(比如访问属性时触发了__getattr__,又间接调用了in)
立即学习“Python免费学习笔记(深入)”;
测试 __contains__ 是否生效的最简方式
别依赖 IDE 提示或文档注释,直接用交互式环境验证:
>>> class Demo: ... def __contains__(self, x): return x == 42 ... >>> d = Demo() >>> 42 in d True >>> 99 in d False >>> "hello" in d # 传任意类型都应安全处理 False
重点测三类输入:存在的值、不存在的值、非预期类型(如 None、嵌套对象)。如果其中任一情况抛异常(比如 AttributeError),说明你的 __contains__ 没做防御性检查。另外,注意 in 对自定义类永远不触发 __eq__ —— 它只看你 __contains__ 返回什么,这点和 list.index() 或 dict.get() 的行为完全不同。
真正容易被忽略的是:Python 在调用 <strong>contains</strong> 前,不会检查你有没有实现 <strong>iter</strong>;但如果类同时定义了两者,in 一定优先用 <strong>contains</strong>。所以问题往往不出在“该不该实现”,而出在“有没有拼对、有没有返回布尔、有没有在所有分支都返回”。

















