gpt4 book ai didi

python - “iter() returned non-iterator” 用于动态绑定(bind) `next` 方法

转载 作者:太空宇宙 更新时间:2023-11-03 13:09:45 24 4
gpt4 key购买 nike

为什么我动态绑定(bind)到类实例的这个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 内部的源代码!

for i in foo 被编译时,生成的字节码将包含 GET_ITER 操作码,它负责将 foo 转换为可迭代对象。 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 t->tp_iter).

因为你已经在类型上实现了一个 __iter__ 函数,这个函数确实存在,所以我们得到上面代码中的 else 案例,它运行 iter 对象 o 上的函数。结果是非空的,但我们仍然收到“返回的非迭代器”消息,因此 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) 恰好不是未实现的 next 函数。

仔细检查检查发生的位置:在结果的类型上,而不是结果本身。 foo 对象确实有一个 next 函数,但它的类型 Foo 没有。

setattr(foo, 'next', MethodType(next, foo, Foo))

……或者更明确地说……

foo.next = next.__get__(foo, Foo)

… 只在实例上设置绑定(bind)的 next 方法 而不是在类型本身上。所以上面的 C 代码将无法将其作为可迭代对象使用。

如果您改为在 type 上设置 next 函数,它将正常工作:

foo = Foo()
Foo.next = next

for i in foo:
print i

这就是为什么您尝试使用 classmethod 成功的原因:您在 type 而不是其具体实例上设置函数。

关于python - “iter() returned non-iterator” 用于动态绑定(bind) `next` 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46281264/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com