c89*_*f64 5 python json dictionary attributeerror
我正在处理加载到 Python 字典中的 JSON 数据。其中很多都有可选字段,其中可能包含字典之类的东西。
dictionary1 =
{"required": {"value1": "one", "value2": "two"},
"optional": {"value1": "one"}}
dictionary2 =
{"required": {"value1": "one", "value2": "two"}}
Run Code Online (Sandbox Code Playgroud)
如果我这样做,
dictionary1.get("required").get("value1")
Run Code Online (Sandbox Code Playgroud)
显然,这是有效的,因为场"required"总是存在的。
但是,当我使用同一行dictionary2(以获取可选字段)时,这将产生一个AttributeError
dictionary2.get("optional").get("value1")
AttributeError: 'NoneType' object has no attribute 'get'
Run Code Online (Sandbox Code Playgroud)
这是有道理的,因为第一个.get()将返回None,而第二个.get()不能调用.get()None 对象。
我可以通过提供默认值来解决这个问题,以防可选字段丢失,但是数据变得越复杂,这就会很烦人,所以我称之为“天真的修复”:
dictionary2.get("optional", {}).get("value1", " ")
Run Code Online (Sandbox Code Playgroud)
因此,第一个.get()将返回一个空字典{},可以在其上调用第二个字典.get(),并且由于它显然不包含任何内容,因此它将返回空字符串,如第二个默认值所定义的那样。
这将不再产生错误,但我想知道是否有更好的解决方案 - 特别是对于更复杂的情况(value1包含数组或另一个字典等......)
我也可以用 try - except 来解决这个问题AttributeError,但这也不是我喜欢的方式。
try:
value1 = dictionary2.get("optional").get("value1")
except AttributeError:
value1 = " "
Run Code Online (Sandbox Code Playgroud)
我也不喜欢检查可选字段是否存在,这会产生垃圾代码行,例如
optional = dictionary2.get("optional")
if optional:
value1 = optional.get("value1")
else:
value1 = " "
Run Code Online (Sandbox Code Playgroud)
这看起来非常非Pythonic......
我在想也许我链接.get()s 的方法一开始就是错误的?
在您的代码中:
try:
value1 = dictionary2.get("optional").get("value1")
except AttributeError:
value1 = " "
Run Code Online (Sandbox Code Playgroud)
您可以使用括号和except KeyError:
try:
value1 = dictionary2["optional"]["value1"]
except KeyError:
value1 = " "
Run Code Online (Sandbox Code Playgroud)
如果这对于调用者来说太冗长,请添加一个助手:
def get_or_default(d, *keys, default=None):
try:
for k in keys:
d = d[k]
except (KeyError, IndexError):
return default
return d
if __name__ == "__main__":
d = {"a": {"b": {"c": [41, 42]}}}
print(get_or_default(d, "a", "b", "c", 1)) # => 42
print(get_or_default(d, "a", "b", "d", default=43)) # => 43
Run Code Online (Sandbox Code Playgroud)
您还可以对 dict 进行子类化并使用元组括号索引,例如 NumPy 和 Pandas:
class DeepDict(dict):
def __init__(self, d, default=None):
self.d = d
self.default = default
def __getitem__(self, keys):
d = self.d
try:
for k in keys:
d = d[k]
except (KeyError, IndexError):
return self.default
return d
def __setitem__(self, keys, x):
d = self.d
for k in keys[:-1]:
d = d[k]
d[keys[-1]] = x
if __name__ == "__main__":
dd = DeepDict({"a": {"b": {"c": [42, 43]}}}, default="foo")
print(dd["a", "b", "c", 1]) # => 43
print(dd["a", "b", "c", 11]) # => "foo"
dd["a", "b", "c", 1] = "banana"
print(dd["a", "b", "c", 1]) # => "banana"
Run Code Online (Sandbox Code Playgroud)
但是,如果其他开发人员感到困惑,并且您想要充实其他预期方法,如如何“完美”覆盖字典?中所述,则可能会产生工程成本。(将此视为概念验证草图)。最好不要太聪明。
首先,您指的" "是空字符串。这是不正确的;""是空字符串。
get其次,如果您要检查会员资格,我首先看不出使用该方法的理由。我会选择类似下面的东西。
if "optional" in dictionary2:
value1 = dictionary2["optional"].get("value1")
else:
value1 = ""
Run Code Online (Sandbox Code Playgroud)
另一种可供考虑的替代方案(因为您get经常使用该方法)是切换到该类defaultdict。例如,
from collections import defaultdict
dictionary2 = {"required": {"value1": "one", "value2": "two"}}
ddic2 = defaultdict(dict,dictionary2)
value1 = ddic2["optional"].get("value1")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2146 次 |
| 最近记录: |