仅定义了“__getitem__”方法的 class 如何支持 'in' 运算符?
How a class having only the '__getitem__' method defined support the 'in' operator?
如果我有以下定义:
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]
如何在未定义 __contains__
dunder 方法的情况下支持 in
运算符。例如以下内容:
deck = CardDeck()
print(Card('2', 'hearts') in deck)
将输出:
True
有什么想法吗?
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__(),...
__getitem__
用作 表达式 参考文档中没有 __contains__
or __iter__
method is available. See the Membership test operations section 时的回退:
Lastly, the old-style iteration protocol is tried: if a class defines __getitem__()
, x in y
is True
if and only if there is a non-negative integer index i such that x is y[i] or x == y[i]
, and no lower integer index raises the IndexError
exception.
所以实际发生的是 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__
方法以提供更好的实施!
对于卡片组,我可以想象当项目是 Card
实例时简单地返回 True
就足够了(前提是 Card
class 验证等级和套装参数):
def __contains__(self, item):
# a card deck contains all possible cards
return isinstance(item, Card)
如果我有以下定义:
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]
如何在未定义 __contains__
dunder 方法的情况下支持 in
运算符。例如以下内容:
deck = CardDeck()
print(Card('2', 'hearts') in deck)
将输出:
True
有什么想法吗?
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__(),...
__getitem__
用作 表达式 参考文档中没有 __contains__
or __iter__
method is available. See the Membership test operations section 时的回退:
Lastly, the old-style iteration protocol is tried: if a class defines
__getitem__()
,x in y
isTrue
if and only if there is a non-negative integer index i such thatx is y[i] or x == y[i]
, and no lower integer index raises theIndexError
exception.
所以实际发生的是 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__
方法以提供更好的实施!
对于卡片组,我可以想象当项目是 Card
实例时简单地返回 True
就足够了(前提是 Card
class 验证等级和套装参数):
def __contains__(self, item):
# a card deck contains all possible cards
return isinstance(item, Card)