我正在使用API调用,因此使用Python字典。
但是,对于同样的请求,我并不总是键入相同的键,我想知道何时可以在没有异常的情况下调用键...
假设我有
test = {'a':{'b':{'c':{'d':'e'}}}}
有时键d会存在,有时则不会。有时c甚至不存在。
我想以某种方式在一行中检查test['a']['b']['c']['d']
是否存在。
到目前为止我尝试过:
使用test.get('a', {}).get('b', {}).get('c', {}).get('d', {})
。工作正常,但它很乱,有时我有5-6个嵌套的字典,名字很长...
使用很好的try / except块,但通常如果test['a']['b']['c']['d']
不存在,我会尝试调用test['a']['b']['e']['f']
来检查是否存在,因此我需要添加一个对我的每个if语句尝试/ catch,好像我没有错,如果异常是catch,则try block不再执行。
我可能试图寻找一种反身的方式来做这件事,调用一个以我的“对象”的名字作为字符串的函数,它将检查每个键是否存在,如果存在,则返回对象本身。
有什么想法吗?
背后的用法是,省略无用的情况,并假设有时信息在测试['a'] ['b'] ['c'] ['d'],有时在测试['a' ] ['b'] ['f']:
if test['a']['b']['c']['d'] **exists**:
do sthg with the value of test['a']['b']['c']['d']
elif test['a']['b']['f'] **exists**:
do sthg else with the value of test['a']['b']['f']
else:
do sthg different
如果我在那里试一试,那么第一个异常不会停止执行并且不让我执行elif吗?
此外,我真的很喜欢调用test['a']['b']['c']['d']
比给出密钥列表更好的方式。事实上,我希望它对我和那些阅读/使用我的代码的人们尽可能透明。
答案 0 :(得分:2)
您可以编写一个递归函数来检查:
def f(d, keys):
if not keys:
return True
return keys[0] in d and f(d[keys[0]], keys[1:])
如果函数返回True,则存在键:
In [10]: f(test,"abcd")
Out[10]: True
In [11]: f(test,"abce")
Out[11]: False
如果您想测试多个组合键:
for keys in ("abce","abcr","abcd"):
if f(test,keys):
print(keys)
break
abcd
要返回值,这很简单:
def f(d, keys):
if len(keys) == 1:
return d[keys[0]] if keys[0] in d else False
return keys[0] in d and f(d[keys[0]], keys[1:])
print(f(test,"abcd"))
e
您可以再次测试多个组合键:
def test_keys(keys):
for keys in keys:
val = f(test,keys)
if val:
return val
return False
print(test_keys(("abce","abcr","abc")))
您也可以迭代地编写函数:
def f(d, keys):
obj = object
for k in keys:
d = d.get(k, obj)
if d is obj:
return False
return d
print(f(test,"abcd"))
e
如果要根据返回值运行条件:
def f(d, keys):
obj = object
for k in keys:
d = d.get(k, obj)
if d is obj:
return False
return d
from operator import mul
my_actions = {"c": mul(2, 2), "d": lambda: mul(3, 3), "e": lambda: mul(3, 3)}
for st in ("abce", "abcd", "abcf"):
val = f(test, st)
if val:
print(my_actions[val]())
9
只需按照与if / elif等相同的顺序测试关键组合。
答案 1 :(得分:1)
这不完全是你想要的,因为它没有检查是否存在,但是这里有一个类似于dict.get
方法的单行:
In [1]: test = {'a':{'b':{'c':{'d':'e'}}}}
In [2]: keys = 'abcd' # or ['a', 'b', 'c', 'd']
In [3]: reduce(lambda d, k: d.get(k) if d else None, keys, test)
Out[3]: 'e'
In [4]: keys = 'abcf'
In [5]: reduce(lambda d, k: d.get(k) if d else None, keys, test)
不幸的是,它效率不高,因为只要缺少其中一个键,它就不会停止。
答案 2 :(得分:1)
如果您正在使用JSON,您可以编写一个简单的类,以便在导入时使用dict。
给出以下JSON:
>>> js='{"a": {"b": {"c": {"d": "e"}}}}'
如果它由对象组成,它通常会被解码成Python dict:
>>> import json
>>> json.loads(js)
{u'a': {u'b': {u'c': {u'd': u'e'}}}}
作为普通的Python dict,它受KeyError
缺少键的约束。您可以使用__missing__
挂钩覆盖KeyErrors
并获得原始结构:
class Mdict(dict):
def __missing__(self, key):
return False
现在测试:
>>> md=Mdict({'a':Mdict({'b':Mdict({'c':Mdict({'d':'e'})})})})
>>> if md['a']['b']['d']:
... print md['a']['b']['d']
... elif md['a']['b']['c']:
... print 'elif', md['a']['b']['c']
...
elif {'d': 'e'}
dict的每个级别都需要Mdict
vs普通Python dict
。但是,如果您正在使用JSON,那么这很容易实现。只需在解码JSON时应用object_pairs_hook:
>>> js
'{"a": {"b": {"c": {"d": "e"}}}}'
>>> md=json.loads(js, object_pairs_hook=Mdict)
当JSON被解码时,这会使用类Mdict
而不是默认的Python dict
。
>>> md
{u'a': {u'b': {u'c': {u'd': u'e'}}}}
>>> md['a']
{u'b': {u'c': {u'd': u'e'}}}
>>> md['a']['c']
False
此处示例的其余部分保持不变。
答案 3 :(得分:0)
我会创建一个递归函数。
def get_key(d, *args):
if not args:
return None
val = d.get(args[0], None)
if len(args) == 1:
return val
if isinstance(val, dict):
return get_key(val, *args[1:])
答案 4 :(得分:0)
您可以嵌套try
块来处理两种丢失键的异常。依赖于try
块符合EAFP(更容易请求宽恕而不是许可)Python的哲学,而不是LBYL(在你跳跃之前看)在使用前测试存在的模式。在多线程程序中,它还有助于避免由另一个线程在存在测试和值的使用之间修改test
字典引起的意外TOCTTOU(检查时间)行为。
try:
value_abcd = test['a']['b']['c']['d']
except KeyError:
try:
value_abf = test['a']['b']['f']
except KeyError:
print("do something different")
else:
print("value_abf is", value_abf)
else:
print("value_abcd is", value_abcd)
从那以后我注意到你有两个以上的钥匙。使用这么多密钥嵌套try
块会创建arrowhead anti-pattern。因此,您可以尝试以下构造来处理同一缩进级别的所有键,只要访问发生在函数或for
循环中,以便它可以return
或{{1} }。如果没有,extract a method。
continue
答案 5 :(得分:0)
另一个是为了完整性,但只递归检查一个键:
def hasKey(d, key):
found = False
if isinstance(d, dict):
for k in d:
found = True if k == key else found or hasKey(d[k], key)
if isinstance(d, list):
for i in d:
found = found or hasKey(i, key)
return found
检查 key
是否作为嵌套字典 map
中的键存在。