for i in foo:
print i
これは失敗したコードなので、のが、内部で何が起こるかを見てみましょう、いくつかのPython内部のソースに飛び込みます!
for i in foo
をコンパイルすると、生成されたバイトコードにfoo
を反復可能に変換するGET_ITER
オペコードが含まれます。 GET_ITER
は、イテラブルを提供する実際の実装であるオブジェクトに対してPyObject_GetIter
コールを発生させます。それでは、what it doesを見てみましょう:
PyObject * PyObject_GetIter(PyObject *o)
{
PyTypeObject *t = o->ob_type;
getiterfunc f = NULL;
if (PyType_HasFeature(t, Py_TPFLAGS_HAVE_ITER))
f = t->tp_iter;
if (f == NULL) {
if (PySequence_Check(o))
return PySeqIter_New(o);
return type_error("'%.200s' object is not iterable", o);
}
else {
PyObject *res = (*f)(o);
if (res != NULL && !PyIter_Check(res)) {
PyErr_Format(PyExc_TypeError,
"iter() returned non-iterator of type '%.100s'",
res->ob_type->tp_name);
Py_DECREF(res);
res = NULL;
}
return res;
}
}
(あなたは、少なくともいくつかの基本的なCを理解していれば)あなたが見ることができるように、基本となる型は最初のオブジェクト(o->ob_type
)から見上げて、そのITER機能があるさ()を読んでください。
あなたがタイプに__iter__
機能を実装しているので、この関数は存在しないので、私たちは、オブジェクトo
にiter
機能を実行し、上記のコードではelse
場合、に着きます。結果はnullではありませんが、まだ "反復されていないイテレータ"メッセージが返されるため、PyIter_Check(res)
は失敗したようです。それでは、what that doesを見てみましょう:
#define PyIter_Check(obj) \
(PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_ITER) && \
(obj)->ob_type->tp_iternext != NULL && \
(obj)->ob_type->tp_iternext != &_PyObject_NextNotImplemented)
渡されたオブジェクトのタイプ(ob_type
)がないことが起こるない非ヌルnext
法(tp_iternext
)を持っているのであればこの1つは、本質的にチェックします - 次の機能を実装しました。
このチェックの結果は、のタイプにありますが、結果自体ではありません。 foo
オブジェクトにはnext
という機能がありますが、そのタイプはFoo
にはありません。
setattr(foo, 'next', MethodType(next, foo, Foo))
...以上明示的に...
foo.next = next.__get__(foo, Foo)
は...唯一のタイプ自体にインスタンスにバインドnext
方法を設定しませんが。したがって、上記のCコードはiterableとして消費するのに失敗します。
あなたの代わりにタイプであなたのnext
機能を設定した場合、それが正常に動作します:
foo = Foo()
Foo.next = next
for i in foo:
print i
これはclassmethod
であなたの試みが働いていた理由です:あなたはタイプに機能を設定します具体的なインスタンスの代わりに
このコードは何をする予定ですか? – poke
次のメソッドをイテレータにするために動的にバインドする必要があります。 – QuantumEnergy
通常は '__double_underscored__'という名前の特別なメソッドですが、Python 2では' next'も含まれています(Python 3では '__next__'という名前に変更されています)、インスタンスではなくクラスで定義する必要があります。たとえば、__add__メソッドを動的に追加しても同様の問題が発生します。 – Blckknght