使用 forbiddenfruit 使 int 可迭代
Make int iterable with forbiddenfruit
我知道,这是错误的,但这可能吗?
当一个对象的 .__iter__
方法返回一个迭代器时,我认为一个对象被认为是可迭代的?那为什么这行不通呢?
>>> 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__
方法:
>>> int(5).__iter__
<bound method int.__iter__ of 5>
for
循环的反汇编是:
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
操作码。
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
:
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
是否无效。
现在,这是让一切都点击的东西:
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
当您在 class 上正常设置属性时,它会调用 PyType_Type->setattro
:
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
绕过了这个过程,只是将字典注入 class。这意味着 PyLong_Type
保留其默认值:
PyTypeObject PyLong_Type = {
...
0, /* tp_iter */
...
};
所以
if (f == NULL)
被触发,
if (PySequence_Check(o))
失败(因为它不是一个序列)然后它只是
return type_error("'%.200s' object is not iterable", o);
我知道,这是错误的,但这可能吗?
当一个对象的 .__iter__
方法返回一个迭代器时,我认为一个对象被认为是可迭代的?那为什么这行不通呢?
>>> 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__
方法:
>>> int(5).__iter__
<bound method int.__iter__ of 5>
for
循环的反汇编是:
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
操作码。
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
:
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
是否无效。
现在,这是让一切都点击的东西:
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
当您在 class 上正常设置属性时,它会调用 PyType_Type->setattro
:
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
绕过了这个过程,只是将字典注入 class。这意味着 PyLong_Type
保留其默认值:
PyTypeObject PyLong_Type = {
...
0, /* tp_iter */
...
};
所以
if (f == NULL)
被触发,
if (PySequence_Check(o))
失败(因为它不是一个序列)然后它只是
return type_error("'%.200s' object is not iterable", o);