Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何在YAML中引用现有变量?_Python_Yaml - Fatal编程技术网

Python 如何在YAML中引用现有变量?

Python 如何在YAML中引用现有变量?,python,yaml,Python,Yaml,假设我的Python脚本中已经定义了一个对象,它充当一些随机项的容器。容器的每个属性对应于一个项。在这个简单的例子中,我有一个ITEMS对象,它有一个BALL属性,该属性指向一个BALL实例 现在,我需要在YAML中加载一些内容,但我希望这些内容能够引用已经定义的现有项变量。这可能吗?也许是类似于 ITEMS = Items() setattr(Items, 'BALL', Ball()) yaml_text = "item1: !!python/object:ITEMS.BALL" yaml

假设我的Python脚本中已经定义了一个对象,它充当一些随机项的容器。容器的每个属性对应于一个项。在这个简单的例子中,我有一个
ITEMS
对象,它有一个
BALL
属性,该属性指向一个
BALL
实例

现在,我需要在YAML中加载一些内容,但我希望这些内容能够引用已经定义的现有
变量。这可能吗?也许是类似于

ITEMS = Items()
setattr(Items, 'BALL', Ball())

yaml_text = "item1: !!python/object:ITEMS.BALL"
yaml_items = yaml.load(yaml_text)

加载YAML后,我的目标是使
YAML_items['item1']
成为
items
对象中的
Ball
实例。

@martineau引用了文档:

[…]提供特定于Python的标记,允许表示任意Python对象

表示,而不是构造。这意味着您可以将任何Python对象转储到YAML,但不能引用YAML中现有的Python对象

也就是说,您当然可以添加自己的构造函数:

import yaml

def eval_constructor(loader, node):
  return eval(loader.construct_scalar(node))

yaml.add_constructor(u'!eval', eval_constructor)

some_value = '123'

yaml_text = "item1: !eval some_value"
yaml_items = yaml.load(yaml_text)
注意
eval
ing配置数据的安全影响。将任意Python代码写入YAML文件即可执行


主要是从

复制过来的。这里有一种方法,它使用了在“到另一个问题”中定义的
di()
函数。它接受从内置的
id()
函数返回的整数值,并将其转换为字符串。
yaml.load()
函数将调用一个自定义构造函数,该构造函数随后执行与该过程相反的操作,以确定返回的对象

警告:这充分利用了一个事实,即至少在使用CPython时,
id()
函数返回内存中Python对象的地址,因此它可能无法与解释器的其他实现一起工作

import _ctypes
import yaml

def di(obj_id):
    """ Reverse of id() function. """
    return _ctypes.PyObj_FromPtr(obj_id)

def py_object_constructor(loader, node):
    return di(int(node.value))

yaml.add_constructor(u'!py_object', py_object_constructor)

class Items(object): pass

def Ball(): return 42

ITEMS = Items()
setattr(Items, 'BALL', Ball())  # Set attribute to result of calling Ball().

yaml_text = "item1: !py_object " + str(id(ITEMS.BALL))
yaml_items = yaml.load(yaml_text)

print(yaml_items['item1'])  # -> 42
如果您同意使用
eval()
,您可以将其形式化,并通过对
yaml
模块的
load()
函数进行猴子补丁来对yaml流进行一些预处理,从而使其更易于使用:

import _ctypes
import re
import yaml

#### Monkey-patch yaml module.
def _my_load(yaml_text, *args, **kwargs):
    REGEX = r'@@(.+)@@'

    match = re.search(REGEX, yaml_text)
    if match:
        obj = eval(match.group(1))
        yaml_text = re.sub(REGEX, str(id(obj)), yaml_text)

    return _yaml_load(yaml_text, *args, **kwargs)

_yaml_load = yaml.load  # Save original function.
yaml.load = _my_load  # Change it to custom version.
#### End monkey-patch yaml module.

def di(obj_id):
    """ Reverse of id() function. """
    return _ctypes.PyObj_FromPtr(obj_id)

def py_object_constructor(loader, node):
    return di(int(node.value))

yaml.add_constructor(u'!py_object', py_object_constructor)

class Items(object): pass

def Ball(): return 42

ITEMS = Items()
setattr(Items, 'BALL', Ball())  # Set attribute to result of calling Ball().

yaml_text = "item1: !py_object @@ITEMS.BALL@@"
yaml_items = yaml.load(yaml_text)
print(yaml_items['item1'])  # -> 42

您使用的第三方YAML库/模块是什么?“除非这是任何东西的一个特性,否则你不可能轻松做到这一点。@martineau我已经用简单的东西尝试了PyYAML和ruamel.yaml,但我们使用的模块仍然是待定的。我只是在看PyYAML模块的功能,它说PyYAML具有完整的YAML 1.1解析器、Unicode支持、pickle支持、功能强大的扩展API和合理的错误消息。PyYAML支持标准的YAML标记,并提供特定于Python的标记,允许表示任意Python对象。”(强调我的),所以听起来你只是需要弄清楚如何做最后一部分。有没有文档,你读过吗?@martineau我在文档中查找了和。有一些方法可以使用python特定的标记,就像你提到的,但我没有看到任何使用我最初提出的现有对象的方法。定义我自己的构造ctor是我考虑过的一种策略,但我希望避免这种情况,这样我就不必自己进行任何文本解析或
eval
ing。尽管如此,我还是很感激这个建议。谢谢!这是一个非常有趣和聪明的实现,我可能会尝试一下。谢谢!还要注意,这不使用
eva()l
,因此比@flyx的答案更安全。对JSON执行类似的操作更好、更容易实现,因为
JSON
模块有一个钩子,允许类似
+str(id(ITEMS.BALL))的内容
部分将更自动地完成。我不太熟悉
yaml
模块,安装它只是为了测试我的答案,因此可能有比此处所示更好的实现方法。