以下代码生成一个无限嵌套的python列表:
import yaml
i_list = yaml.load('&id1 [3, *id1]')
print i_list
# [3, [...]]
print i_list[1] is i_list
# True
我还可以明确提到python list
类型:
i_list = yaml.load('&id1 !!python/list [3, *id1]')
我还可以在不解析yaml的情况下手动创建该结构,如下所示:
i_list = [3]
i_list.append(i_list)
但是,最后一个技巧不适用于元组或任何其他不可变对象。要创建一个无限嵌套的元组,我必须使用CPython的API:
from ctypes import pythonapi
from _ctypes import PyObj_FromPtr
t = pythonapi.PyTuple_New(1)
pythonapi.PyTuple_SetItem(t, 0, t)
i_tup = PyObj_FromPtr(t)
print repr(i_tup)
# ((...),)
这样一个元组的预期yaml代码看起来像这样:
&id001 !!python/tuple
- *id001
实际上,这是yaml.dump(i_tup)
的输出。但是,python的yaml无法加载相同的代码:
yaml.load(yaml.dump(i_tup))
ConstructorError: found unconstructable recursive node
in "<string>", line 1, column 1:
&id001 !!python/tuple
^
有这么好的理由吗?您可以建议的任何解决方法吗?
答案 0 :(得分:4)
元组根本就不是为此而设计的。没有办法通过普通的Python API构建这样的东西,甚至可以让你绕过它的C API有一个检查(op->ob_refcnt != 1
),如果你尝试,它很可能会破坏: / p>
int
PyTuple_SetItem(register PyObject *op, register Py_ssize_t i, PyObject *newitem)
{
register PyObject *olditem;
register PyObject **p;
if (!PyTuple_Check(op) || op->ob_refcnt != 1) {
Py_XDECREF(newitem);
PyErr_BadInternalCall();
return -1;
}
...
}
如果在插入自引用后尝试使用此函数设置元组的任何项,Python会将自引用检测为错误。像这样构建元组需要您自担风险,如果您的代码因为奇怪的原因而中断,请不要感到惊讶。