为什么正则表达式匹配对象不可迭代,即使它们实现了 __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
被初始化为 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
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
。
如您所知,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
被初始化为 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
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
。