web-dev-qa-db-ja.com

なぜ__getitem__を実装しているのに正規表現一致オブジェクトが反復可能でないのですか?

ご存知かもしれませんが、 __getitem__メソッドを実装するとクラスが反復可能になります

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__を実装するにはどうすればよいですか?

52
Aran-Fey

嘘、ひどい嘘、そして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()組み込み関数は、1つの引数で呼び出された場合、次のコードを持つ _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_スロット(明らかに__SRE_Match_オブジェクトのNULL)をチェックします。それに失敗すると、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_は__SRE_Match_インスタンスに対してNULLであったため、0が返され、_PyObject_GetIter_は_TypeError: '_sre.SRE_Match' object is not iterable_を発生させます。

52
Antti Haapala