我有一个YAML配置文件,其中包含一个字典,如下所示:
"COLUMN_NAME": column_function
它将字符串映射到函数(存在并且应该被调用)。
但是,当我使用yaml
加载它时,我看到加载的字典现在将字符串映射到字符串:
'COLUMN_NAME': 'column_function'
现在我无法按预期使用它 - 'column_function'
并未指向column_function
。
加载dict
以便映射到我的函数的好方法是什么?在对此问题进行搜索和阅读之后,我对使用eval
或类似的东西非常谨慎,因为配置文件是用户编辑的。
我认为this thread是关于我的问题,但我不确定最佳方法。
getattr
和setattr
已经出局了,因为它们是在实例化对象上运行的,而我有一个简单的脚本。 globals()
,vars()
和locals()
为我提供了dict
个变量,根据{{3},我想我应该使用globals()
}。 我应该在配置dict
中的每个键值对中查找其中的字符串吗?这是一个好方法:
for (key, val) in STRING_DICTIONARY.items():
try:
STRING_DICTIONARY[key] = globals()[val]
except KeyError:
print("The config file specifies a function \"" + val
+ "\" for column \"" + key
+ "\". No such function is defined, however. ")
答案 0 :(得分:0)
要查找名称val
并以通用方式对其进行评估,我将使用以下内容:
def fun_call_by_name(val):
if '.' in val:
module_name, fun_name = val.rsplit('.', 1)
# you should restrict which modules may be loaded here
assert module_name.startswith('my.')
else:
module_name = '__main__'
fun_name = val
try:
__import__(module_name)
except ImportError as exc:
raise ConstructorError(
"while constructing a Python object", mark,
"cannot find module %r (%s)" % (utf8(module_name), exc), mark)
module = sys.modules[module_name]
fun = getattr(module, fun_name)
return fun()
这是从ruamel.yaml.constructor.py:find_python_name()
改编而来的,用于从字符串标量创建对象。如果交付的val
包含一个点,则会假设您在另一个模块中查找函数名称。
但我不会从你的顶级词典中神奇地解释数值。 YAML具有标记机制(对于特定标记,find_python_name()
方法将起作用,以控制所创建的实例的类型。)
如果您可以控制YAML文件的外观,请使用标记选择性地不创建字符串,如此文件input.yaml
中所示:
COLUMN_NAME: !fun column_function # tagged
PI_VAL: !fun my.test.pi # also tagged
ANSWER: forty-two # this one has no tag
假设子目录my
的文件test.py
包含内容:
import math
def pi():
return math.pi
您可以使用:
import sys
import ruamel.yaml
def column_function():
return 3
def fun_constructor(loader, node):
val = loader.construct_scalar(node)
return fun_call_by_name(val)
# add the constructor for the tag !fun
ruamel.yaml.add_constructor('!fun', fun_constructor, Loader=ruamel.yaml.RoundTripLoader)
with open('input.yaml') as fp:
data = ruamel.yaml.round_trip_load(fp)
assert data['COLUMN_NAME'] == 3
ruamel.yaml.round_trip_dump(data, sys.stdout)
得到:
COLUMN_NAME: 3 # tagged
PI_VAL: 3.141592653589793 # also tagged
ANSWER: forty-two # this one has no tag
如果您不关心将data
作为YAML转储并保留评论,则可以使用SafeLoader
和safe_load()
代替RoundTripLoader
resp。 round_trip_loader()
。