"What are the possible use cases?" 用于检查生成器是否会产生某个值。
Dunder方法作为与特定语法相关联的“钩子”。__contains__并不是一种一对一映射到x in y的方法。语言最终定义了这些运算符的语义。
从
成员测试文档中,我们可以看到有几种评估x in y的方法,取决于涉及的对象的各种属性。我已经突出了生成器对象的相关属性,它们没有定义__contains__但是可迭代,即它们定义了一个__iter__方法:
运算符 in 和 not in 用于测试成员资格。表达式
x in s
的值为 True,如果 x 是序列 s 的成员,否则返回 False。表达式
x not in s
返回其反值。所有内置的序列和集合类型都支持这种成员资格测试,包括字典,其中 in 操作符检查字典是否包含指定的键。对于诸如列表、元组、集合、冻结集、字典或 collections.deque 等容器类型,表达式 x in y 等效于
any(x is e or x == e for e in y)
。
对于字符串和字节类型,当且仅当 x 是 y 的子串时,表达式
x in y
的值为 True。等价的测试是
y.find(x) != -1
。空字符串始终被认为是任何其他字符串的子串,因此
"" in "abc"
将返回 True。
对于定义了 __contains__() 方法的用户定义类,如果 y.__contains__(x) 返回 true 值,则 x in y 返回 True,否则返回 False。
对于未定义 contains() 但定义了 __iter__() 的用户定义类,如果在迭代 y 时产生了某个值 z,使得表达式
x is z or x == z
为真,则 x in y 返回 True。如果在迭代过程中引发异常,则好像 in 引发了该异常。
最后,尝试旧式迭代协议:如果一个类定义了 __getitem__(),则当且仅当存在非负整数索引 i 使得
x is y[i] or x == y[i]
,且没有更低的整数索引引发 IndexError 异常时,x in y 的值为 True。如果引发任何其他异常,则好像 in 引发了该异常。
运算符 not in 的定义与 in 相反。
总之,x in y
的定义适用于以下对象:
- 字符串或字节,其定义为子字符串关系。
- 定义了
__contains__
的类型。
- 迭代器类型,即定义了
__iter__
的类型。
- 旧式迭代协议(依赖于
__getitem__
)。
生成器属于第三种情况。
更广泛的观点是,除非你真正理解它们在做什么,否则你不应该直接使用dunder方法。即使你真的理解了,最好还是避免使用。
通常,通过使用类似以下内容来增加可信度或简洁性并不值得:
x.__lt__(y)
代替:
x < y
你至少应该明白,这种情况可能发生:
>>> (1).__lt__(3.)
NotImplemented
>>>
如果你只是天真地做一些像 filter((1).__lt__, iterable)
这样的事情,那么你可能有一个错误。
in
只是尝试消耗它,直到找到元素并返回True,或者生成器用完了,此时返回False。这似乎是从生成器中获得非常直观、预期的行为。 - Omer Tuchfeld