首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用禁忌果使int可迭代

用禁忌果使int可迭代
EN

Stack Overflow用户
提问于 2015-04-12 15:50:55
回答 1查看 850关注 0票数 6

我知道,这是错误的,但有可能吗?当对象的.__iter__方法返回迭代器时,我认为对象是可迭代的吗?那为什么这个不起作用?

代码语言:javascript
复制
>>> from forbiddenfruit import curse
>>> def __iter__(self):
...     for i in range(self):
...         yield i
>>> curse(int, "__iter__", __iter__)
>>> for x in 5:
...     print x
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

int现在似乎确实有了一个__iter__方法:

代码语言:javascript
复制
>>> int(5).__iter__
<bound method int.__iter__ of 5>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-04-12 21:43:03

for循环的反汇编是:

代码语言:javascript
复制
import dis

dis.dis("for _ in _: pass")
#>>>   1           0 SETUP_LOOP              14 (to 17)
#>>>               3 LOAD_NAME                0 (_)
#>>>               6 GET_ITER
#>>>         >>    7 FOR_ITER                 6 (to 16)
#>>>              10 STORE_NAME               0 (_)
#>>>              13 JUMP_ABSOLUTE            7
#>>>         >>   16 POP_BLOCK
#>>>         >>   17 LOAD_CONST               0 (None)
#>>>              20 RETURN_VALUE

所以我们要GET_ITER操作码。

代码语言:javascript
复制
TARGET(GET_ITER) {
    /* before: [obj]; after [getiter(obj)] */
    PyObject *iterable = TOP();
    PyObject *iter = PyObject_GetIter(iterable);
    Py_DECREF(iterable);
    SET_TOP(iter);
    if (iter == NULL)
        goto error;
    PREDICT(FOR_ITER);
    DISPATCH();
}

它使用PyObject_GetIter

代码语言:javascript
复制
PyObject *
PyObject_GetIter(PyObject *o)
{
    PyTypeObject *t = o->ob_type;
    getiterfunc f = NULL;
    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;
    }
}

这首先检查t->tp_iter的无效性。

现在,让一切都点击的东西是:

代码语言:javascript
复制
class X:
    pass

X.__iter__ = lambda x: iter(range(10))

list(X())
#>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

from forbiddenfruit import curse

class X:
    pass

curse(X, "__iter__", lambda x: iter(range(10)))

list(X())
#>>> Traceback (most recent call last):
#>>>   File "", line 16, in <module>
#>>> TypeError: 'X' object is not iterable

当您通常在类上设置属性时,它会调用PyType_Type->setattro

代码语言:javascript
复制
static int
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{
    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
        PyErr_Format(
            PyExc_TypeError,
            "can't set attributes of built-in/extension type '%s'",
            type->tp_name);
        return -1;
    }
    if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
        return -1;
    return update_slot(type, name);
}

看到update_slot了吗?这会更新插槽,因此下一次对GET_ITER的调用将在X上命中tp->tp_iter。但是,forbiddenfruit绕过了这个过程,只向类中注入了一个字典。这意味着PyLong_Type保留了它的默认设置:

代码语言:javascript
复制
PyTypeObject PyLong_Type = {
    ...
    0,                                          /* tp_iter */
    ...
};

所以

代码语言:javascript
复制
if (f == NULL)

被触发,

代码语言:javascript
复制
if (PySequence_Check(o))

失败(因为它不是序列),然后它只是

代码语言:javascript
复制
return type_error("'%.200s' object is not iterable", o);
票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29591349

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档