如何在 YAML 中引用现有变量? [英] How to reference an existing variable in YAML?

查看:93
本文介绍了如何在 YAML 中引用现有变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Let's say I have an object already defined in my Python script that serves as a container for some random items. Each attribute of the container corresponds to an item. In this simple example, I have an ITEMS object that has a BALL attribute which points to a Ball instance.

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

Now, I need to load some content in YAML, but I want that content to be able to reference the existing ITEMS variable that is already defined. Is this possible? Maybe something along the lines of...

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 实例.

My goal, after loading the YAML, is for yaml_items['item1'] to be the Ball instance from the ITEMS object.

推荐答案

这是一种使用 回答另一个问题.它获取从内置 id() 函数返回的整数值并将其转换为字符串.yaml.load() 函数将调用自定义构造函数,然后执行该过程的相反过程以确定返回的对象.

Here's a way of doing it the uses the di() function defined in the answer to another question. It takes the integer value returned from the built-in id() function and converts it to a string. The yaml.load() function will call a custom constructor which then does the reverse of that process to determine the object returned.

警告:这利用了这样一个事实,至少在 CPython 中,id() 函数返回 Python 对象在内存中的地址——因此它可能不适用于解释器的其他实现.

Caveat: This takes advantage of the fact that, with CPython at least, the id() function returns the address of the Python object in memory—so it may not work with other implementations of the interpreter.

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 流进行一些预处理:

If you're OK with using eval(), you could formalize this and make it easier to use by monkey-patching the yaml module's load() function to do some preprocessing of the yaml stream:

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 中引用现有变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆