如何在Python中检查变量是否为字典?
这是一个很好的问题,但不幸的是,最受欢迎的答案以一个糟糕的建议开头:type(obj) is dict
。
(请注意,您也不应该将dict
用作变量名 - 这是内置对象的名称。)
如果您编写的代码将被其他人导入和使用,请不要假定他们将直接使用内置的dict - 这样做会使您的代码更加死板,在这种情况下,可能会创建一些难以发现的错误而不会使程序出错。
基于正确性、可维护性和为未来用户提供灵活性的目的,我强烈建议在代码中永远不要使用不够灵活、不符合惯用表达方式的表达式,尽管有更加灵活、惯用的表达方式。
is
是对对象身份进行测试的方法。它不支持继承,不支持任何抽象,也不支持接口。
因此,我将提供几个支持这些特性的选项。
支持继承:
这是我推荐的第一个方法,因为它允许用户提供自己的字典子类,或来自collections模块的OrderedDict
、defaultdict
或Counter
:
if isinstance(any_object, dict):
但还有更加灵活的选项。
支持抽象:
from collections.abc import Mapping
if isinstance(any_object, Mapping):
这样可以让您的代码用户使用自己定制的抽象映射实现(包括任何dict
的子类),并且仍然获得正确的行为。
使用接口
通常会听到面向对象编程的建议,"按照接口编程"。
这种策略利用了Python的多态性或鸭子类型。因此,只需尝试访问接口,捕获特定的预期错误(AttributeError
表示没有.items
,TypeError
表示items
不可调用),并具有合理的回退 - 现在,实现该接口的任何类都将给您其项(请注意.iteritems()
在Python 3中已经不存在):
try:
items = any_object.items()
except (AttributeError, TypeError):
non_items_behavior(any_object)
else:
for item in items: ...
也许您认为像这样使用鸭子类型会允许太多的误判,但这取决于您对该代码的目标。
结论
不要使用is
来检查标准控制流程的类型。请使用isinstance
,考虑抽象类别如Mapping
或MutableMapping
,并考虑直接使用接口避免进行类型检查。
isinstance(v, collections.abc.Mapping)
。换句话说,这不是“isinstance()和type()之间的区别”的完全重复。 - Josh Kelley