solo - validar expresiones regulares en python
¿Por qué los objetos de coincidencia de expresiones regulares no son iterables aunque implementan__getitem__? (1)
Como sabrá, la
implementación de un método
__getitem__
hace que una clase sea 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
Sin embargo, esto no es cierto para los objetos de coincidencia de expresiones regulares:
>>> 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
Vale la pena señalar que esta excepción no se produce en el método
__iter__
, porque ese método ni siquiera se implementa:
>>> hasattr(match, ''__iter__'')
False
Entonces, ¿cómo es posible implementar
__getitem__
sin hacer que la clase sea iterable?
Hay mentiras, malditas mentiras y luego está la documentación de Python.
Tener
__getitem__
para una clase implementada en
C
no es suficiente para que sea iterable.
Esto se debe a que en realidad hay
2
lugares en el
PyTypeObject
donde el
__getitem__
se puede asignar a:
tp_as_sequence
y
tp_as_mapping
.
Ambos tienen una ranura para
__getitem__
(
[1]
,
[2]
).
Mirando la fuente de
SRE_Match
,
tp_as_sequence
se inicializa a
NULL
mientras que
tp_as_mapping
está definido.
La función incorporada
iter()
, si se llama con un argumento, llamará a
PyObject_GetIter
, que tiene el siguiente código:
f = t->tp_iter;
if (f == NULL) {
if (PySequence_Check(o))
return PySeqIter_New(o);
return type_error("''%.200s'' object is not iterable", o);
}
Primero verifica la ranura
tp_iter
(obviamente
NULL
para los objetos
_SRE_Match
);
y en su defecto, entonces
si
PySequence_Check
devuelve true, un nuevo iterador de secuencia, de lo contrario, se
TypeError
un
TypeError
.
PySequenceCheck
primero verifica si el objeto es un
dict
o una subclase de
dict
, y en ese caso devuelve false.
De lo contrario, devuelve el valor de
s->ob_type->tp_as_sequence &&
s->ob_type->tp_as_sequence->sq_item != NULL;
y como
s->ob_type->tp_as_sequence
era
NULL
para una instancia
_SRE_Match
, se devolverá 0, y
PyObject_GetIter
genera
TypeError: ''_sre.SRE_Match'' object is not iterable
.