2

如果我定义了以下内容:

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)

将输出:

True

有任何想法吗?

4

2 回答 2

3

__getitem____contains__当没有或__iter__方法可用时用作后备。请参阅表达式参考文档的成员资格测试操作部分:

最后,尝试了旧式迭代协议:如果一个类定义了__getitem__(),当且x in yTrue当存在一个非负整数索引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该类验证了等级和花色参数):

def __contains__(self, item):
    # a card deck contains all possible cards
    return isinstance(item, Card)
于 2019-07-14T14:35:11.670 回答
2

文档

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__(),...

请参阅语言参考中的此部分

于 2019-07-14T14:28:44.860 回答