为生成器定义的“in”

skg*_*nga 6 python python-3.8

为什么要in为生成器定义运算符?

>>> def foo():
...     yield 42
... 
>>> 
>>> f = foo()
>>> 10 in f
False
Run Code Online (Sandbox Code Playgroud)

可能的用例有哪些?

我知道range(...)对象__contains__定义了一个函数,这样我们就可以做这样的事情:

>>> r = range(10)
>>> 4 in r
True
>>> r.__contains__
<method-wrapper '__contains__' of range object at 0x7f82bd51cc00>
Run Code Online (Sandbox Code Playgroud)

f上面没有__contains__方法。

jua*_*aga 4

“可能的用例是什么?” 检查生成器是否会产生一些值。

Dunder 方法充当与其关联的特定语法的挂钩。__contains__不是某种到 的一对一映射x in y。该语言最终定义了这些运算符的语义。

成员资格测试的文档中,我们看到有多种x in y评估方法,具体取决于所涉及对象的各种属性。我已经强调了生成器对象的相关内容,这些对象没有定义 a__contains__ 但可以迭代,即它们定义了一个__iter__方法:

正在或未参加会员资格测试的运营商。x in s计算结果为 Truex 是否为 s 的成员,否则为 False。x not in s返回 的否定x in s。所有内置序列和集合类型都支持此功能以及字典,其中测试字典是否具有给定的键。对于列表、元组、集合、frozenset、dict 或 collections.deque 等容器类型,y 中的表达式 x 等效于any(x is e or x == e for e in y)

对于 string 和 bytes 类型,x in yisTrue当且仅当x是 的子字符串y。等效测试是y.find(x) != -1. 空字符串始终被视为任何其他字符串的子字符串,因此"" in "abc"将返回 True。

对于定义该__contains__()方法的用户定义类,True如果y.__contains__(x)返回真值,则返回 x in y,False 否则返回。

对于未定义contains () 但定义了定义的用户定义类__iter__()x in y如果在迭代 时生成 表达式为true 的True某个值。如果在迭代期间引发异常,就好像引发了该异常。zx is z or x == zy

最后,尝试旧式迭代协议:如果一个类定义 __getitem__()x in yTrue当且仅当存在一个非负整数索引使得ix is y[i] or x == y[i]并且没有更低的整数索引引发 IndexError 异常。(如果引发任何其他异常,则就像引发该异常一样)。

该运算符not in被定义为具有 in 的逆真值。

总而言之,x in y将为以下对象定义:

  1. 是字符串或者字节,定义为子串关系。
  2. 定义的类型__contains__
  3. 作为迭代器的类型,即定义__iter__
  4. 旧式迭代协议(依赖于__getitem__

发电机分为 3 种。

更广泛的一点是,您确实不应该直接使用 dunder 方法,除非您真正了解它们在做什么。即便如此,最好还是避免这种情况。

通常不值得尝试通过使用某些东西来达到可信或简洁的效果:

x.__lt__(y)
Run Code Online (Sandbox Code Playgroud)

代替:

x < y
Run Code Online (Sandbox Code Playgroud)

您至少应该明白,这可能会发生:

>>> (1).__lt__(3.)
NotImplemented
>>>
Run Code Online (Sandbox Code Playgroud)

如果您只是天真地做这样的事情,filter((1).__lt__, iterable) 那么您可能遇到了错误