如何在Python中检查一个变量是否为字典?

396
如何在Python中检查一个变量是否为字典?
例如,我希望它能够循环遍历字典中的值,直到找到一个字典为止。然后,再循环遍历找到的那个字典。
dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}

for k, v in dict.items():
    if ###check if v is a dictionary:
        for k, v in v.iteritems():
            print(k, ' ', v)
    else:
        print(k, ' ', v)

还有https://dev59.com/NXRC5IYBdhLWcg3wMeHf(被标记为上面那个问题的重复)。 - NPE
也可以访问 https://dev59.com/AnE95IYBdhLWcg3wn_f2 - bereal
50
不,这不是同一个问题。这个问题以及这里列出的其他问题的答案都包含了实质上相同的信息。但是,“如何检查变量是否为Python字典”的答案是“使用type()或isinstance()”,这就引发了一个新问题,即type()和isinstance()之间的区别是什么。但提出第一个问题的人在第一个问题得到回答之前不可能知道这一点。因此,这是不同的问题,在网站上寻找您的问题时这很重要。 - user2177304
14
我同意@mbakeranalecta的观点。我来到这里是为了寻找答案:“如何在Python中检查一个变量是否为字典?”我从未想过要在“isinstance()和type()在Python中的区别”中寻找答案。请帮我翻译成中文。 - lodebari
8
为了检查一个变量是否为字典,你应该使用isinstance(v, collections.abc.Mapping)。换句话说,这不是“isinstance()和type()之间的区别”的完全重复。 - Josh Kelley
5个回答

464
使用isinstance(ele, dict),它将对dict对象以及dict的子类(如OrderedDictdefaultdict)返回true:
d = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}
for element in d.values():
    if isinstance(element, dict):
       for k, v in element.items():
           print(k, ' ', v)

如果你想严格检查是否为字典的实例而不是其子类,你可以使用if type(ele) is dict,尽管这通常是不推荐的做法。

137
我将此回答投下反对票,因为针对这个一般问题的正确答案是:isinstance(ele, collections.Mapping)。它适用于dict()collections.OrderedDict()collections.UserDict()。虽然问题中的示例足够具体,可以使用Padriac的答案,但对于一般情况来说,这已经不够好了。 - Alexander Ryzhov
9
我对这个答案进行了踩票,因为如果你要调用 ele.items() ,那么为什么还要检查类型?EAFP/鸭子类型在这里起作用,只需在 for k,v in ele.items() 外面包裹 try...except (AttributeError, TypeError)。如果引发异常,你就知道 ele 没有提供一个可迭代的 items... - cowbert
1
@PadraicCunningham 我宁愿不教那些对Python不太熟悉的人反模式。在Python中,几乎没有什么用例是_需要_显式类型检查的 - 大多数情况都源于一开始就继承了一个糟糕的实现('god object's,覆盖标准库/语言结构等)。原始问题本身就是一个XY问题。为什么OP需要检查类型?因为根据他们的代码,他们真正想做的是检查集合中的项是否_像_集合一样(实现了items()并产生嵌套可迭代对象)。 - cowbert
8
@AlexanderRyzhov,为什么不把这种方法作为答案发布呢?另外,正如Josh Kelley在上面评论中提到的那样,建议使用collections.abc.Mapping来检查字典。需要注意的是,在Python 3.3之前,collections.abc是不可用的。在Python 3.6中仍然可以使用collections.Mapping别名,但没有记录,因此最好使用collections.abc.Maping - Yushin Washio
@AlexanderRyzhov isinstance(collections.OrderedDict(), dict) 返回为真,与 defaultdict 相同。但是对于 UserDict 不是这样。 - user3064538
显示剩余2条评论

103

如何在Python中检查变量是否为字典?

这是一个很好的问题,但不幸的是,最受欢迎的答案以一个糟糕的建议开头:type(obj) is dict

(请注意,您也不应该将dict用作变量名 - 这是内置对象的名称。)

如果您编写的代码将被其他人导入和使用,请不要假定他们将直接使用内置的dict - 这样做会使您的代码更加死板,在这种情况下,可能会创建一些难以发现的错误而不会使程序出错。

基于正确性、可维护性和为未来用户提供灵活性的目的,我强烈建议在代码中永远不要使用不够灵活、不符合惯用表达方式的表达式,尽管有更加灵活、惯用的表达方式。

is是对对象身份进行测试的方法。它不支持继承,不支持任何抽象,也不支持接口。

因此,我将提供几个支持这些特性的选项。

支持继承:

这是我推荐的第一个方法,因为它允许用户提供自己的字典子类,或来自collections模块的OrderedDictdefaultdictCounter

if isinstance(any_object, dict):

但还有更加灵活的选项。

支持抽象:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

这样可以让您的代码用户使用自己定制的抽象映射实现(包括任何dict的子类),并且仍然获得正确的行为。

使用接口

通常会听到面向对象编程的建议,"按照接口编程"。

这种策略利用了Python的多态性或鸭子类型。因此,只需尝试访问接口,捕获特定的预期错误(AttributeError表示没有.itemsTypeError表示items不可调用),并具有合理的回退 - 现在,实现该接口的任何类都将给您其项(请注意.iteritems()在Python 3中已经不存在):

try:
    items = any_object.items()
except (AttributeError, TypeError):
    non_items_behavior(any_object)
else: # no exception raised
    for item in items: ...

也许您认为像这样使用鸭子类型会允许太多的误判,但这取决于您对该代码的目标。

结论

不要使用is来检查标准控制流程的类型。请使用isinstance,考虑抽象类别如MappingMutableMapping,并考虑直接使用接口避免进行类型检查。


精心撰写的解释选择,以及何时/为什么选择它们。 - Jesse Chisholm
我认为草率地得出“不要使用is来检查标准控制流类型”的结论还为时过早。我们需要逐个案例进行评估。在某些情况下,更希望有严格的检查控制,以确保它是唯一的类型,但不包括其子类型。 - Cheok Yan Cheng
目前,随着mypy的静态类型检查和match支持继承的情况出现,这个答案需要更新一下,需要更多全面的细节。 - Russia Must Remove Putin

7
在Python 3.6中
    typeVariable = type(variable)
    
    print('comparison',typeVariable == dict)

    if typeVariable == dict:
        #'true'
    else:
        #'false'

6

OP没有排除起始变量,因此为了完整起见,这里介绍如何处理可能包含字典项的字典。

还要遵循纯Python(3.8)推荐的方法在上面的注释中进行字典测试。

from collections.abc import Mapping

my_dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}

def parse_dict(in_dict): 
    if isinstance(in_dict, Mapping):
        for k_outer, v_outer in in_dict.items():
            if isinstance(v_outer, Mapping):
                for k_inner, v_inner in v_outer.items():
                    print(k_inner, v_inner)
            else:
                print(k_outer, v_outer)

parse_dict(my_dict)

1
dict.iteritems()在Python 3中不存在,您应该使用dict.items()代替。 - Arkelis
@Arkelis 抱歉,我刚才只是复制/粘贴了那部分内容,感谢指出 - 现在已经更正了。 - CodeMantle
它与 typing.Mapping 有何不同? - tejasvi88
@tejasvi88 看起来是优化与通用方法。该集合基于抽象基类,包括上面的字典等“已知”集合。通用替代方案(看起来是一个较新的提议)可能会使其更加通用于用户定义的类/集合。希望对您有所帮助。 - CodeMantle
typing.Mapping 自 Python 3.9 起已被弃用,请参阅文档。 - eugenhu
显示剩余2条评论

4

我的测试发现,现在有了类型提示,这个功能可以正常工作:

from typing import Dict

if isinstance(my_dict, Dict):
    # True
else:
    # False

顺便提一下,关于typing.Dict的讨论在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接