Python __getitem__和in运算符导致奇怪的行为


34

是什么解释了以下行为:

class Foo:
    def __getitem__(self, item):
        print("?")
        return 1

f = Foo()

1 in f  # prints one ? and returns True

5 in f  # prints ? forever until you raise a Keyboard Exception

# Edit: eventually this fails with OverflowError: iter index too large

Answers:


45

如果对象没有__contains__实现,请in使用默认的默认设置,该默认设置基本上是这样的:

def default__contains__(self, element):
    for thing in self:
        if thing == element:
            return True
    return False

如果对象没有__iter__实现,则for使用默认的默认设置,该默认设置基本上是这样的:

def default__iter__(self):
    i = 0
    try:
        while True:
            yield self[i]
            i += 1
    except IndexError:
        pass

即使对象不是序列,也将使用这些默认值。

你1 in f和5 in f测试使用的是默认的回退in和for,导致观察到的行为。立即1 in f发现1,但您__getitem__永不归来5,因此5 in f永远运行。

(实际上,在Python的参考实现中,默认的__iter__后备存储将索引存储在类型为C的变量中Py_ssize_t,因此,如果等待足够长的时间,该变量将最大化,并且Python会引发OverflowError。如果看到了,您可以必须在32位Python版本上运行。计算机的存在时间不足以使任何人都能在64位Python上运行它。)


关于OverflowError,我在64位和32位上都运行了,您是对的,我只在32位上看到它。
— 马修·莫森

您是否偶然知道解释此内容的文档?我想阅读一下为什么要决定实施此方案。
— 马修·莫森


4
@MatthewMoisen:这些默认值是原来的行为for和in,早于引进__iter__和__contains__。在此处和此处查看Python 1.4文档。
— user2357112支持Monica19年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.