您可能知道,implementing a __getitem__
method makes a class iterable:
class IterableDemo:
def __getitem__(self, index):
if index > 3:
raise IndexError
return index
demo = IterableDemo()
print(demo[2]) # 2
print(list(demo)) # [0, 1, 2, 3]
print(hasattr(demo, '__iter__')) # False
但是,这不适用于正则表达式匹配对象:
>>> import re
>>> match = re.match('(ab)c', 'abc')
>>> match[0]
'abc'
>>> match[1]
'ab'
>>> list(match)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '_sre.SRE_Match' object is not iterable
值得注意的是,__iter__
方法中没有抛出此异常,因为该方法甚至没有实现:
>>> hasattr(match, '__iter__')
False
那么,如何在不使类可迭代的情况下实现__getitem__
?
答案 0 :(得分:51)
有谎言,该死的谎言,然后有Python文档。
对于在 C 中实现的类,拥有__getitem__
不足以使其可迭代。这是因为PyTypeObject
中实际上有 2 个地方,其中__getitem__
可以映射到:tp_as_sequence
和tp_as_mapping
。两者都有一个用于__getitem__
([1],[2])的插槽。
查看SRE_Match
的来源,将tp_as_sequence
初始化为NULL
,而定义tp_as_mapping
。
iter()
内置函数(如果使用一个参数调用)将调用PyObject_GetIter
,它具有以下代码:
f = t->tp_iter;
if (f == NULL) {
if (PySequence_Check(o))
return PySeqIter_New(o);
return type_error("'%.200s' object is not iterable", o);
}
它首先检查tp_iter
插槽(显然NULL
是_SRE_Match
个对象);否则,如果 if PySequence_Check
返回true,则使用新的序列迭代器,否则引发TypeError
。
PySequenceCheck
首先检查对象是 dict
还是dict
子类-在这种情况下返回false。否则返回
s->ob_type->tp_as_sequence &&
s->ob_type->tp_as_sequence->sq_item != NULL;
并且由于s->ob_type->tp_as_sequence
实例的NULL
是_SRE_Match
,因此将返回0,并且PyObject_GetIter
引发TypeError: '_sre.SRE_Match' object is not iterable
。