获取yaml中定义或未定义值的Python方法

获取yaml中定义或未定义值的Python方法,python,yaml,pyyaml,Python,Yaml,Pyyaml,我有一个带有测试配置的yaml文件,在可选部分“测试选项”中有一个可选参数“ignore dup txn” 我阅读了“测试名称”部分到“测试”部分,现在我这样检查: if 'test-options' in test and 'ignore-dup-txn' in test['test-options']: ignore_dups = test['test-options']['ignore-dup-txn'] else: ignore_dups = None 做这件事的pyt

我有一个带有测试配置的yaml文件,在可选部分“测试选项”中有一个可选参数“ignore dup txn”

我阅读了“测试名称”部分到“测试”部分,现在我这样检查:

if 'test-options' in test and 'ignore-dup-txn' in test['test-options']:
    ignore_dups = test['test-options']['ignore-dup-txn']
else:
    ignore_dups = None
做这件事的pythonic方法是什么?更加清晰、简单和简短


我本来想做“getter”,但是如果我做了
get(test['test-option']['ignore-dup-txn'])
,如果没有定义选项,我会得到一个异常。显然,你可以使用
get
方法:

class ExtendedDict(dict):
    def multi_level_get(self, key, default=None):
        if not isinstance(key, list):
            return self.get(key, default)
        # assume that the key is a list of recursively accessible dicts
        # *** using [] and not .get() in the following on purpose ***
        def get_one_level(key_list, level, d):
            if level >= len(key_list):
                if level > len(key_list):
                    raise IndexError
                return d[key_list[level-1]]
            return get_one_level(key_list, level+1, d[key_list[level-1]])

        try:
            return get_one_level(key, 1, self)
        except KeyError:
            return default

    get = multi_level_get # delete this if you don't want to mask get()
                          # you can still use the multi_level-get()

d = dict(a=dict(b=dict(c=42)))
assert d['a']['b']['c'] == 42

try:
    d['a']['xyz']['c'] == 42
except KeyError as e:
    assert e.message == 'xyz'
else:
    raise NotImplementedError

ed = ExtendedDict(d)
assert ed['a']['b']['c'] == 42
assert ed.get(['a', 'b', 'c'], 196) == 42
assert ed.get(['a', 'xyz', 'c'], 196) == 196 # no execption!
test['test-options'].get('ignore-dup-txn',
默认值

这将起作用:

test.get('test-options', {}).get('ignore-dup-txn', None)
如果您只想要一个“一行程序”,而不想创建一个空的dict,您可以执行以下操作:

ignore_dups = test['test-options'].get('ignore-dup-txn') if 'test-options' in test else None
但这会导致长长的线条,并且不能很好地扩展到另一个层次,也不是很像蟒蛇

对于我来说更像Python的东西,首先看看当你有一个
dict
并使用一个列表作为赋值键或
.get()的第一个参数时会发生什么:

d = dict()
l = ['a', 'b', 'c']
try:
    d[l] = 3
except TypeError as e:
    assert e.message == "unhashable type: 'list'"
else:
    raise NotImplementedError
try:
    d.get(l, None)
except TypeError as e:
    assert e.message == "unhashable type: 'list'"
else:
    raise NotImplementedError
这意味着
某些dict.get(['a','b','c',默认值)
将抛出一个类型错误。另一方面,这是一个相当简洁的语法,可以从一个dict中获取一个dict中的值
所以问题变成了我如何才能让这样的
.get()
工作

首先,您必须意识到您不能只替换
dict
上的
.get()
方法,您将得到一个
AttributeError

d = dict()
def alt_get(key, default):
    pass
try:
    d.get = alt_get
except AttributeError as e:
    assert e.message == "'dict' object attribute 'get' is read-only"
else:
    raise NotImplementedError
因此,您必须将
dict
子类化,这允许您重写
.get()
方法:

class ExtendedDict(dict):
    def multi_level_get(self, key, default=None):
        if not isinstance(key, list):
            return self.get(key, default)
        # assume that the key is a list of recursively accessible dicts
        # *** using [] and not .get() in the following on purpose ***
        def get_one_level(key_list, level, d):
            if level >= len(key_list):
                if level > len(key_list):
                    raise IndexError
                return d[key_list[level-1]]
            return get_one_level(key_list, level+1, d[key_list[level-1]])

        try:
            return get_one_level(key, 1, self)
        except KeyError:
            return default

    get = multi_level_get # delete this if you don't want to mask get()
                          # you can still use the multi_level-get()

d = dict(a=dict(b=dict(c=42)))
assert d['a']['b']['c'] == 42

try:
    d['a']['xyz']['c'] == 42
except KeyError as e:
    assert e.message == 'xyz'
else:
    raise NotImplementedError

ed = ExtendedDict(d)
assert ed['a']['b']['c'] == 42
assert ed.get(['a', 'b', 'c'], 196) == 42
assert ed.get(['a', 'xyz', 'c'], 196) == 196 # no execption!
当只在dict中递归地包含dict时,这种方法可以很好地工作,但当您将这些与列表混合使用时,这种方法也可以在有限的范围内工作:

e = dict(a=[dict(c=42)])
assert e['a'][0]['c'] == 42
ee = ExtendedDict(e)
# the following works becauuse get_one_level() uses [] and not get()
assert ee.get(['a', 0, 'c'], 196) == 42
try:
    ee.get(['a', 1, 'c'], 196) == 42
except IndexError as e:
    assert e.message == 'list index out of range'
else:
    raise NotImplementedError
try:
    ee.get(['a', 'b', 'c'], 196) == 42
except TypeError as e:
    assert e.message == 'list indices must be integers, not str'
else:
    raise NotImplementedError
当然,您也可以在
multi_level_get()
中捕获后两个错误,方法是使用
except(KeyError、TypeError、indexer):
并返回默认值 对于所有这些情况

在²中,此多级get实现为
mlget()
(这需要一个额外的参数以允许列表成为层次结构的一部分):

其中打印:

excel

在示例中,我宁愿使用断言来确认正在发生的事情,而不是打印语句,然后对打印的内容进行单独解释。这使得解释更加简洁,对于
中的断言,try
/
except
块明确抛出异常,而不破坏代码并禁止执行以下代码。所有示例代码都来自运行的python文件,只打印一个值。

²我是这个软件包的作者,它是PyYAML的增强版。

ignore\u dups=test['test-options'].get('ignore-dup-txn')
?如果你不介意分配一个空的dict,你可以尝试
test.get(“test options”),get(“ignore-dup-txn”)
我不介意分配一个空的dict。它是有效的。谢谢如果未定义可选部分“测试选项”,它将失败
test.get('test-options',{}).get('ignore-dup-txn',None)
from comments works。
excel