在没有 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.urluser.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 方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

python字典嵌套字典的情况下获取某个key的value

python字典嵌套字典的情况下获取某个key的value

在给定“路径”的情况下修改嵌套字典中的元素

如何在 Swift 中访问深度嵌套的字典

有没有办法使用连接的字符串键访问嵌套字典? [复制]

Python - 将字典列表附加到嵌套的默认字典时出现关键错误