python – “iter()返回非迭代器”用于动态绑定`next`方法
为什么我动态绑定到类实例的下一个方法失败并返回非迭代器对象?
from collections import Iterator from collections import Iterable from types import MethodType def next(cls): if cls.start < cls.stop: cls.start += 1 return cls.start else: raise StopIteration class Foo(object): start,stop = 0,5 def __iter__(self): return self if __name__ == "__main__": foo = Foo() setattr(foo,'next',MethodType(next,foo,Foo)) print hasattr(foo,"next") if isinstance(foo,Iterable): print "iterable" if isinstance(foo,Iterator): print "iterator" for i in foo: print i 输出: iterable True TypeError: iter() returned non-iterator of type 'Foo' 它工作正常,当我做setattr(Foo,’next’,classmethod(next)). 解决方法for i in foo: print i 这是失败的代码,所以让我们来看看内部发生的事情,深入了解一些Python内部的源代码! 当编译foo中的i时,生成的字节码将包含GET_ITER操作码,该操作码负责将foo转换为可迭代的. GET_ITER对对象进行 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函数(t-> tp_iter). 由于你已经在类型上实现了__iter__函数,所以这个函数确实存在,所以我们在上面的代码中得到了else的情况,它在对象o上运行iter函数.结果是非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)是否具有非空的下一个方法(tp_iternext),这个方法恰好不是未实现的下一个函数. 仔细检查发生检查的位置:在结果的类型上,而不是结果本身. foo对象确实有下一个函数,但它的类型Foo没有. setattr(foo,Foo)) ……或者更明确…… foo.next = next.__get__(foo,Foo) …仅在实例上设置绑定的next方法,但不在类型本身上设置.因此,上面的C代码将无法将其作为可迭代使用. 如果您要在类型上设置下一个函数,它可以正常工作: foo = Foo() Foo.next = next for i in foo: print i 这就是你尝试使用classmethod的原因:你在类型上设置函数而不是它的具体实例. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |