使用默认方法优雅地加载 YAML 配置
Gracefully loading YAML config with default methods
我正在尝试找到一种优雅的方法来将存储在 YAML 文件中的值加载到我的 __init__
函数中,用于多个 类。如果我有一个包含类似内容的 YAML 文件:
#YAML
an_object:
var_a: 1
var_b: 2
具有以下模块:
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
我想使用 YAML 文件中的值(如果它们存在),否则使用 __init__
方法默认值(如果它们不存在)。我还希望它扩展到许多变量 (>10),所以我不想 try/except 每个参数。
我的第一反应是:
import yaml
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
with open('config.yaml','r') as stream:
cfg = yaml.load(stream)['an_object']
for key, value in cfg.iteritems():
if hasattr(self, key):
exec("self.{} = {}".format(key, value))
但我宁愿不必使用 exec
,也不想将变量赋值两次(这有点令人困惑)。有一个更好的方法吗?它还会覆盖非默认函数值,我也不想这样做。
你应该使用 setattr()
:
import yaml
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
with open('config.yaml','r') as stream:
cfg = yaml.load(stream)['an_object']
for key, value in cfg.iteritems():
if hasattr(self, key):
setattr(self, key, value)
请注意,这会将值设置为 YAML 确定文件中的值的类型。所以你必须注意这是否符合你在对象中的期望,或者进行显式转换。
另请注意,对于此 class 的每个实例,YAML 文件都会被打开并读取,因此效率极低。如果您创建许多对象并针对不同的 classes,您应该使所有这些 classes 成为一个 Loader
class 的子 classes,这是一个单例,或者从 class 变量中的 yaml 中读取值,并读入一次值,然后您可以从那里的值(而不是从文件中)在每个实例化对象中执行此更新。
要防止在 YAML 中存在值时分配默认值,您必须首先测试这些值是否存在,如果不存在则采用默认值:
import ruamel.yaml as yaml
class Loader(object):
_val = None
@property
def yaml_values(self):
if Loader._val is None:
with open('config.yaml','r') as stream:
Loader._val = yaml.load(stream)
return Loader._val
def get_val(self, object_id, key, default):
try:
return self.yaml_values[object_id][key]
except KeyError:
return default
class SomeClass(Loader):
obj_id = 'an_object'
def __init__(self, var_a=10, var_b=20, var_c=30):
for key in ['var_a', 'var_b', 'var_c']:
setattr(self, key,
self.get_val(SomeClass.obj_id, key, locals()[key]))
def __repr__(self):
return 'SomeClass(var_a={}, var_b={}, var_c={})'.format(
self.var_a, self.var_b, self.var_c)
sc = SomeClass()
print sc
将打印:
SomeClass(var_a=1, var_b=2, var_c=30)
我意识到最好在创建对象时使用配置,而不是将 YAML 放在 __init__
中:
import yaml
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
if __name__ == 'main':
with open('config.yaml','r') as stream:
cfg = yaml.load(stream)
sc = SomeClass(**cfg)
这会保留默认值,唯一的主要冲突是分配多个关键字,但这是一个简单的解决方法。
我正在尝试找到一种优雅的方法来将存储在 YAML 文件中的值加载到我的 __init__
函数中,用于多个 类。如果我有一个包含类似内容的 YAML 文件:
#YAML
an_object:
var_a: 1
var_b: 2
具有以下模块:
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
我想使用 YAML 文件中的值(如果它们存在),否则使用 __init__
方法默认值(如果它们不存在)。我还希望它扩展到许多变量 (>10),所以我不想 try/except 每个参数。
我的第一反应是:
import yaml
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
with open('config.yaml','r') as stream:
cfg = yaml.load(stream)['an_object']
for key, value in cfg.iteritems():
if hasattr(self, key):
exec("self.{} = {}".format(key, value))
但我宁愿不必使用 exec
,也不想将变量赋值两次(这有点令人困惑)。有一个更好的方法吗?它还会覆盖非默认函数值,我也不想这样做。
你应该使用 setattr()
:
import yaml
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
with open('config.yaml','r') as stream:
cfg = yaml.load(stream)['an_object']
for key, value in cfg.iteritems():
if hasattr(self, key):
setattr(self, key, value)
请注意,这会将值设置为 YAML 确定文件中的值的类型。所以你必须注意这是否符合你在对象中的期望,或者进行显式转换。
另请注意,对于此 class 的每个实例,YAML 文件都会被打开并读取,因此效率极低。如果您创建许多对象并针对不同的 classes,您应该使所有这些 classes 成为一个 Loader
class 的子 classes,这是一个单例,或者从 class 变量中的 yaml 中读取值,并读入一次值,然后您可以从那里的值(而不是从文件中)在每个实例化对象中执行此更新。
要防止在 YAML 中存在值时分配默认值,您必须首先测试这些值是否存在,如果不存在则采用默认值:
import ruamel.yaml as yaml
class Loader(object):
_val = None
@property
def yaml_values(self):
if Loader._val is None:
with open('config.yaml','r') as stream:
Loader._val = yaml.load(stream)
return Loader._val
def get_val(self, object_id, key, default):
try:
return self.yaml_values[object_id][key]
except KeyError:
return default
class SomeClass(Loader):
obj_id = 'an_object'
def __init__(self, var_a=10, var_b=20, var_c=30):
for key in ['var_a', 'var_b', 'var_c']:
setattr(self, key,
self.get_val(SomeClass.obj_id, key, locals()[key]))
def __repr__(self):
return 'SomeClass(var_a={}, var_b={}, var_c={})'.format(
self.var_a, self.var_b, self.var_c)
sc = SomeClass()
print sc
将打印:
SomeClass(var_a=1, var_b=2, var_c=30)
我意识到最好在创建对象时使用配置,而不是将 YAML 放在 __init__
中:
import yaml
class SomeClass(object):
def __init__(self, var_a=10, var_b=20, var_c=30):
self.var_a = var_a
self.var_b = var_b
self.var_c = var_c
if __name__ == 'main':
with open('config.yaml','r') as stream:
cfg = yaml.load(stream)
sc = SomeClass(**cfg)
这会保留默认值,唯一的主要冲突是分配多个关键字,但这是一个简单的解决方法。