375

How would you check if a variable is a dictionary in Python?

For example, I'd like it to loop through the values in the dictionary until it finds a dictionary. Then, loop through the one it finds:

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}
for k, v in dict.iteritems():
    if ###check if v is a dictionary:
        for k, v in v.iteritems():
            print(k, ' ', v)
    else:
        print(k, ' ', v)
Riley
  • 4,122
  • 3
  • 16
  • 30
  • Also http://stackoverflow.com/questions/378927/what-is-the-best-idiomatic-way-to-check-the-type-of-a-python-variable (which is marked as a duplicate of the one above). – NPE Aug 10 '14 at 19:03
  • Also http://stackoverflow.com/q/2225038/770830 – bereal Aug 10 '14 at 19:05
  • 49
    No, it is not the same question. The answer to this question and to the other questions listed here all contain substantially the same information. But the answer to "How to check if a variable is a dictionary in python" is "Use type() or isinstance()" which then leads to a new question, which is what is the difference between type() and isinstance(). But the person asking the first question can't possibly know that until the first question is answered. Ergo, different questions, which matters when you are looking for your question on the site. –  Jan 06 '16 at 22:44
  • 14
    I agree with @mbakeranalecta I came here looking for the answer to the question "How to check is a variable is a dictionary in Python?" and I would have never thought to look my answer into "Differences between isinstance() and type() in python". – lodebari Jan 14 '16 at 14:46
  • 8
    For checking if a variable is a dictionary in particular, you should probably use `isinstance(v, collections.abc.Mapping)`. In other words, this is not an exact duplicate of "Differences between isinstance() and type()." – Josh Kelley May 01 '17 at 20:08

5 Answers5

448

You could use if type(ele) is dict or use isinstance(ele, dict) which would work if you had subclassed dict:

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)
Neuron
  • 5,141
  • 5
  • 38
  • 59
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • 132
    I down-voted this answer because the right answer to the general question is: `isinstance(ele, collections.Mapping)`. It works for `dict()`, `collections.OrderedDict()`, and `collections.UserDict()`. The example in the question is specific enough for Padriac's answer to work, but it's not good enough for the general case. – Alexander Ryzhov Jun 28 '18 at 05:31
  • 9
    I downvoted this answer because if you are going to to invoke `ele.items()` why are you checking type? EAFP/duck-typing works here, just wrap `for k,v in ele.items()` in `try...except (AttributeError, TypeError)`. If exception is raised, you know `ele` has no `items` that yields an iterable... – cowbert Jul 14 '18 at 03:31
  • @Padraic Cunningham Your hypothetical edge case would require that your "100% not a dict" custom class 1. had an `items()` method 2. which just so happened to yield an iterable and 3. where each element of that iterable can be represented as a 2-tuple. At this point your custom object has already implemented half of a dict already. For the purposes of the code listed we only cared about conditional expansion of the nested element, and your custom object already implements that. (It's a bit ironic that you criticize @Alexander Ryzhov's comment for being too general but now raise a general case) – cowbert Jul 14 '18 at 16:17
  • 1
    @PadraicCunningham I'd rather not teach people-who-are-not-overly-familiar-with-Python anti-patterns to begin with. There are very few use-cases in Python that _require_ explicit typechecking - most stem from inheriting a bad implementation to begin with ('god object's, overriding standard library/language constructs, etc.) The original question is itself an XY problem. Why does the OP need to check type? Because according to their code what they really want to do is to check whether an item in their collection _behaves_ like a collection (implements `items()` that yields a nested iterable). – cowbert Jul 14 '18 at 21:02
  • 8
    @AlexanderRyzhov Why not post that approach as an answer? BTW as Josh Kelley [has commented above](https://stackoverflow.com/questions/25231989/how-to-check-if-a-variable-is-a-dictionary-in-python#comment74495204_25231989) suggesting `collections.abc.Mapping`, a note might be appropriate that they’re the same, but `collections.abc` is not available before Python 3.3. `collections.Mapping` alias is still available in Python 3.6, but not documented, so probably one should prefer `collections.abc.Maping`. – Yushin Washio Feb 26 '19 at 12:27
  • I agree with Alexander Ryzhov's comment above and also downvoted the answer. – Andreas Maier Jul 23 '20 at 22:33
95

How would you check if a variable is a dictionary in Python?

This is an excellent question, but it is unfortunate that the most upvoted answer leads with a poor recommendation, type(obj) is dict.

(Note that you should also not use dict as a variable name - it's the name of the builtin object.)

If you are writing code that will be imported and used by others, do not presume that they will use the dict builtin directly - making that presumption makes your code more inflexible and in this case, create easily hidden bugs that would not error the program out.

I strongly suggest, for the purposes of correctness, maintainability, and flexibility for future users, never having less flexible, unidiomatic expressions in your code when there are more flexible, idiomatic expressions.

is is a test for object identity. It does not support inheritance, it does not support any abstraction, and it does not support the interface.

So I will provide several options that do.

Supporting inheritance:

This is the first recommendation I would make, because it allows for users to supply their own subclass of dict, or a OrderedDict, defaultdict, or Counter from the collections module:

if isinstance(any_object, dict):

But there are even more flexible options.

Supporting abstractions:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

This allows the user of your code to use their own custom implementation of an abstract Mapping, which also includes any subclass of dict, and still get the correct behavior.

Use the interface

You commonly hear the OOP advice, "program to an interface".

This strategy takes advantage of Python's polymorphism or duck-typing.

So just attempt to access the interface, catching the specific expected errors (AttributeError in case there is no .items and TypeError in case items is not callable) with a reasonable fallback - and now any class that implements that interface will give you its items (note .iteritems() is gone in Python 3):

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

Perhaps you might think using duck-typing like this goes too far in allowing for too many false positives, and it may be, depending on your objectives for this code.

Conclusion

Don't use is to check types for standard control flow. Use isinstance, consider abstractions like Mapping or MutableMapping, and consider avoiding type-checking altogether, using the interface directly.

Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
  • Well written explanation of the choices, and when/why to choose them. – Jesse Chisholm Sep 16 '20 at 19:47
  • I think it is too quick to jump into conclusion that "Don't use is to check types for standard control flow". We need to evaluate case-by-case basics. In some case, it is more desirable to have a strict check control, where you want to ensure it is the 1 and only 1 type, but not including its children. – Cheok Yan Cheng Apr 14 '21 at 10:31
  • At this point with mypy's static type checking and `match` coming out with cases that support inheritance, this answer needs to be updated somewhat with a bit more nuance all around. – Russia Must Remove Putin Apr 14 '21 at 13:50
7

In python 3.6

    typeVariable = type(variable)
    
    print('comparison',typeVariable == dict)

    if typeVariable == dict:
        #'true'
    else:
        #'false'
MappaM
  • 809
  • 8
  • 15
David
  • 307
  • 3
  • 7
5

The OP did not exclude the starting variable, so for completeness here is how to handle the generic case of processing a supposed dictionary that may include items as dictionaries.

Also following the pure Python(3.8) recommended way to test for dictionary in the above comments.

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)
CodeMantle
  • 1,249
  • 2
  • 16
  • 25
  • 1
    `dict.iteritems()` does not exist in Python 3, you should use `dict.items()` instead. – Arkelis May 06 '20 at 14:31
  • @Arkelis Oops, just copied/pasted that part, thanks for pointing it out - corrected now. – CodeMantle May 07 '20 at 08:22
  • A comment on cosmetics -- To my eyes, the re-use of `k,v` in nested loops looks funky. It also means that the inner `print(k,v)` does't represent the outer `k` in the displayed list. – Jesse Chisholm Sep 16 '20 at 19:50
  • @JesseChisholm, completely right, needs adjusting (done)! – CodeMantle Sep 16 '20 at 23:24
  • How is it different from `typing.Mapping`? – tejasvi88 Jul 06 '21 at 05:15
  • @tejasvi88 It looks like an optimised vs generic approach. The collections builds on Abstract Base Classes for 'known' collections including dict above. The generic alternative (looks like a newer proposition) presumably makes this more generic to user-defined classes/collections. Hth – CodeMantle Jul 06 '21 at 09:56
  • [`typing.Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping) is deprecated since Python 3.9 it seems, see docs. – eugenhu Oct 05 '21 at 11:45
4

My testing has found this to work now we have type hints:

from typing import Dict

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

Side note some discussion about typing.Dict here

Cam
  • 1,263
  • 13
  • 22