为什么正则表达式匹配对象不可迭代,即使它们实现了 __getitem__?

How come regex match objects aren't iterable even though they implement __getitem__?

如您所知,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

那么,如何在不使 class 可迭代的情况下实现 __getitem__

有谎言,该死的谎言,还有 Python 文档。

C 中实现 class 的 __getitem__ 不足以使其可迭代。那是因为在 PyTypeObject 中实际上有 2 个地方 __getitem__ 可以映射到: tp_as_sequence and tp_as_mapping. Both have a slot for __getitem__ ([1], [2]).

查看 SRE_Match 的来源,tp_as_sequence 被初始化为 NULLtp_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 returns true,一个新的序列迭代器,否则会引发 TypeError

PySequenceCheck 首先检查对象是 dict 还是 dict subclass - 和 returns 在这种情况下为假。否则它 returns 的值

s->ob_type->tp_as_sequence &&
    s->ob_type->tp_as_sequence->sq_item != NULL;

并且由于 s->ob_type->tp_as_sequence 对于 _SRE_Match 实例是 NULL,因此将返回 0,并且 PyObject_GetIter 引发 TypeError: '_sre.SRE_Match' object is not iterable