仅定义了'__getitem__'方法的类如何支持'in'运算符?

时间:2019-07-14 14:12:26

标签: python operator-overloading python-collections

如果我定义了以下内容:

header_text = self.browser.find_element_by_tag_name('h1').text

self.assertIn('To-do', header_text) # >>> OK

如何在没有定义Card = namedtuple('Card', ['rank', 'suit']) class CardDeck(): ranks = [str(x) for x in range(2, 11)] + list('JQKA') suits = 'spades diamonds clubs hearts'.split() def __init__(self): self._cards = [Card(rank, suit) for rank in self.ranks for suit in self.suits] def __getitem__(self, index): return self._cards[index] dunder方法的情况下支持in运算符。例如以下内容:

__contains__

将输出:

deck = CardDeck()
print(Card('2', 'hearts') in deck)

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

当没有__contains____iter__ method可用时,

__getitem__用作后备。请参见表达式参考文档的Membership test operations section

  

最后,尝试使用旧式的迭代协议:如果一个类定义了__getitem__(),则当且仅当存在非负整数索引时,x in y才是True i 使得x is y[i] or x == y[i]且没有更低的整数索引会引发IndexError异常。

所以实际上发生的是Python只是使用一个递增的索引,在Python中看起来像这样:

from itertools import count

def contains_via_getitem(container, value):
    for i in count():   # increments indefinitely
        try:
            item = container[i]
            if value is item or value == item:
                return True
        except IndexError:
            return False

此处理方法扩展到所有迭代功能。未实现__iter__但确实实现__getitem__的容器仍可以为其创建一个迭代器(使用iter()或等效的C-API):

>>> class Container:
...     def __init__(self):
...         self._items = ["foo", "bar", "baz"]
...     def __getitem__(self, index):
...         return self._items[index]
...
>>> c = Container()
>>> iter(c)
<iterator object at 0x1101596a0>
>>> list(iter(c))
['foo', 'bar', 'baz']

通过迭代进行的容器测试当然不是很有效。如果有一种方法可以在不进行全面扫描的情况下确定容器中是否有物品,请实施__contains__方法以提供更好的实现!

对于卡片组,我可以想象一下,当项目为True实例时简单地返回Card就足够了(只要Card类可以验证rank和suit参数):< / p>

def __contains__(self, item):
    # a card deck contains all possible cards
    return isinstance(item, Card)

答案 1 :(得分:2)

来自the documemtation

object.__contains__(self, item)   ...

 For objects that don’t define __contains__(), the membership test
 first tries iteration via __iter__(), then the old sequence iteration
 protocol via __getitem__(),...

see this section in the language reference