从YAML加载无限嵌套的元组

时间:2014-07-16 11:21:44

标签: python nested yaml self-contained

以下代码生成一个无限嵌套的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
    ^

有这么好的理由吗?您可以建议的任何解决方法吗?

1 个答案:

答案 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会将自引用检测为错误。像这样构建元组需要您自担风险,如果您的代码因为奇怪的原因而中断,请不要感到惊讶。