在没有 NoneType 错误的情况下访问嵌套字典的 pythonic 方法是啥
Posted
技术标签:
【中文标题】在没有 NoneType 错误的情况下访问嵌套字典的 pythonic 方法是啥【英文标题】:What is the pythonic way to access nested dicts without NoneType errors在没有 NoneType 错误的情况下访问嵌套字典的 pythonic 方法是什么 【发布时间】:2013-02-08 19:31:39 【问题描述】:我有一个深度嵌套的字典(从 json 解码,从 instagram api 解码)。 我最初的代码是这样的:
caption = post['caption']['text']
但是如果 'caption' 键或 'text' 键不存在,则会引发 NoneType 或 KeyError 错误。
所以我想出了这个:
caption = post.get('caption', ).get("text")
哪个有效,但我不确定它的风格。例如,如果我将此技术应用于我试图检索的更深层次的嵌套属性之一,它看起来很丑:
image_url = post.get('images',).get('standard_resolution',).get('url')
有没有更好、更 Pythonic 的方式来写这个?我的目标是检索数据(如果存在),但不阻止执行(如果不存在)。
谢谢!
【问题讨论】:
为什么不能只捕获异常? 我可以。我猜是因为我要拉~7 个键,我不想尝试/除了 7 次。 相关:Python: Change values in dict of nested dicts using items in a list 【参考方案1】:Python 3.4 和更新版本包含一个 contextlib 上下文管理器suppress
,它正是用于这种事情。当您提前知道它们可能会发生并且您的代码可以处理它时,抑制特定错误。
from contextlib import suppress
sample = 'foo': 'bar'
with suppress(KeyError):
print(sample['baz'])
将阻止KeyError
被提升。
因此,为了获取深度嵌套的字典值,您可以像这样使用suppress
。
value = None
with suppress(KeyError):
value = data['deeply']['nested']['dictionary']['key']
【讨论】:
使用单个with suppress(KeyError):
一次获取多行嵌套键的最简洁和 Pythonic 解决方案【参考方案2】:
我会创建一个自定义 dict 子类,然后解决这个问题:
class SafeDict(dict):
def __getitem__(self,k):
if k in self:
return dict.__getitem__(self,k)
return None
a = SafeDict('a':'a')
print a['a']
>> a
print a['b']
>> None
您可以执行自定义 init 来处理嵌套字典作为 SafeDict 的另一个实例(这将允许您传递它们),或者您可以使用测试(或 try/except 块)来处理防止 KeyErrors
另外,你可以把它变成一个对象类,重载 __getattr__
,然后用点符号处理事情。我更喜欢这种方法(我第一次看到这个是在 Pylons 框架中)
class AttributeSafeObject(object):
def __init__(self,**kwargs):
for key in kwargs:
setattr(self,key,kwargs[key])
def __getattr__(self, name):
try:
return object.__getattribute__(self,name)
except AttributeError:
return None
post = AttributeSafeObject('a':'a')
print post.a
>> a
print post.title
>> None
【讨论】:
post
dict 来自 simplejson,我不确定如何让 simplejson 返回 SafeDict,或将标准 dict 转换为 SafeDict。
__getitem__()
的代码会像return self.get(k)
一样简单(你主要是重写get()
方法)。无论如何,这确实回答了问题,因为即使a = SafeDict('a': SafeDict('b': 'b'))
在a['c']['d']
上也失败了,这是问题要求解决的问题。
如果你退后一步......你会注意到调用 AnyClass(yourdict) 真的调用 AnyClass.__init__
与 dict 作为 kwargs。如果你从 dict
继承一个类,这些 kwargs 将成为 dict。如果你从对象继承,你可以享受 init 的乐趣。就个人而言,我可能会使用对象符号。它使 api 编程变得更加容易。
@EOL 不错。我在回复中指出,我明确不包括递归。这只是一个可能追求的想法。【参考方案3】:
最 Pythonic 的方式就是捕捉 KeyError
:
try:
caption = post['caption']['text']
except KeyError:
caption = None
这对 Python 程序员来说简单、明显且立即可以理解。
【讨论】:
请不要将try:
和except:
的正文与各自的介绍放在同一行。它是 PEP 8 的绝妙之一,并且本身就是非pythonic。
已修复,向 PEP 8 道歉。
有没有办法将这个概括为多个键?例如如果我想检索 caption.text
以及 images.standard_resolution.url
和 user.username
以及其他一些,我是否必须执行 n try/except 块?
您可以定义一个函数来检索带有 try/except 的键列表。【参考方案4】:
你对这样的事情感觉如何
if 'caption' in post:
caption = post['caption']['text']
但它也开始崩溃
if 'images' in post and 'standard_resolution' in post['images']:
image_url = post['images']['standard_resolution']['url']
所以我认为最 Pythonic 的方式就是 ask for forgiveness and not permission
try:
image_url = post['images']['standard_resolution']['url']
except KeyError:
image_url = None
【讨论】:
不要使用裸除非,否则会发生有趣的坏事。 (例如KeyboardInterrupt
被吞了)以上是关于在没有 NoneType 错误的情况下访问嵌套字典的 pythonic 方法是啥的主要内容,如果未能解决你的问题,请参考以下文章