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