Python “;iter()返回非迭代器”;对于动态绑定的'next'方法
为什么动态绑定到类实例的Python “;iter()返回非迭代器”;对于动态绑定的'next'方法,python,python-2.7,iterator,python-2.x,python-internals,Python,Python 2.7,Iterator,Python 2.x,Python Internals,为什么动态绑定到类实例的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 StopI
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
当我执行setattr(Foo,'next',classmethod(next))时,它工作正常
这是失败的代码,所以让我们看看内部发生了什么,深入了解一些Python内部的源代码
编译foo中i的时,生成的字节码将包含负责将foo
转换为iterable的GET_ITER
操作码GET_ITER
导致对对象的调用,该对象是提供iterable的实际实现。让我们来看看:
PyObject*PyObject\u GetIter(PyObject*o)
{
PyTypeObject*t=o->ob\u类型;
getiterfunc f=NULL;
if(PyType_HasFeature(t,Py_TPFLAGS_HAVE_ITER))
f=t->tp_iter;
如果(f==NULL){
if(PySequence_Check(o))
返回Pysekiter_New(o);
返回类型_错误(“%.200s”对象不可编辑”,o);
}
否则{
PyObject*res=(*f)(o);
如果(res!=NULL&!PyIter_检查(res)){
PyErr_格式(PyExc_类型错误,
“iter()返回了类型为“%.100s”的非迭代器,
res->ob_类型->tp_名称);
Py_DECREF(res);
res=NULL;
}
返回res;
}
}
如您所见(如果您至少了解一些基本的C语言),首先从对象中查找底层类型(o->ob\u type
),然后读取其iter函数(t->tp\u iter
)
由于您已经在类型上实现了一个\uuuu iter\uuuu
函数,因此该函数确实存在,因此我们在上面的代码中使用了else
案例,它在对象o
上运行iter
函数。结果是非空的,但我们仍然得到“返回的非迭代器”消息,因此PyIter\u检查(res)
似乎失败了。让我们来看看:
#定义PyIter#u检查(obj)\
(PyType_HasFeature((obj)->ob_type,Py_TPFLAGS_HAVE_ITER)和\
(obj)->ob_type->tp_iternext!=NULL&&\
(obj)->ob_type->tp_iternext!=&u PyObject_nextn未实现)
因此,这个方法基本上检查传递对象的类型(ob\u type
)是否有一个非空的next
方法(tp\u iternext
),该方法恰好不是未实现的下一个函数
但仔细检查检查发生的位置:在结果的类型上,而不是在结果本身上。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
成功的原因:您在类型上设置了函数,而不是它的具体实例。这段代码甚至应该做什么?我想动态绑定下一个方法,使其成为迭代器。特殊方法,通常是\uuuuu双下划线\uuuu
名称,但是在Python2中还包括next
(在Python3中重命名为\uuuuuuuuuuuuuuuuuuuuuuuuuuuuu
),必须在类上定义,而不是在实例上定义。例如,动态添加\uuuuuuuuuuuuuu
方法也会遇到类似的问题。
foo.next = next.__get__(foo, Foo)
foo = Foo()
Foo.next = next
for i in foo:
print i