Morgan Harris
Morgan Harris

Reputation: 2619

What's the pythonic way to distinguish between a dict and a list of dicts?

So, I'm trying to be a good Python programmer and duck-type wherever I can, but I've got a bit of a problem where my input is either a dict or a list of dicts.

I can't distinguish between them being iterable, because they both are.

My next thought was simply to call list(x) and hope that returned my list intact and gave me my dict as the only item in a list; alas, it just gives me the list of the dict's keys.

I'm now officially out of ideas (short of calling isinstance which is, as we all know, not very pythonic). I just want to end up with a list of dicts, even if my input is a single solitary dict.

Upvotes: 5

Views: 1574

Answers (5)

Chris Martin
Chris Martin

Reputation: 30736

You could check for the presence of an items attribute.

dict has it and list does not.

>>> hasattr({}, 'items')
True

>>> hasattr([], 'items')
False

Here's a complete list of the differences in attribute names between dict and list (in Python 3.3.2).

Attributes on list but not dict:

>>> print('\n'.join(sorted(list(set(dir([])) - set(dir({}))))))
__add__
__iadd__
__imul__
__mul__
__reversed__
__rmul__
append
count
extend
index
insert
remove
reverse
sort

Attributes on dict but not list:

>>> print('\n'.join(sorted(list(set(dir({})) - set(dir([]))))))
fromkeys
get
items
keys
popitem
setdefault
update
values

Upvotes: 1

bcollins
bcollins

Reputation: 3459

Don't use the types module:

import types

d = {}
print type(d) is types.DictType

l = [{},{}]
print type(l) is types.ListType and len(l) and type(l[0]) is types.DictType

Upvotes: -1

kindall
kindall

Reputation: 184211

Accessing a dict using a non-int key will get you either an item, or a KeyError. It will get you a TypeError with a list. So you can use exception handling:

def list_dicts(dict_or_list):
    try:
        dict_or_list[None]
        return [dict_or_list]  # no error, we have a dict
    except TypeError:
        return dict_or_list    # wrong index type, we have a list
    except Exception:
        return [dict_or_list]  # probably KeyError but catch anything to be safe

This function will give you a list of dicts regardless of whether it got a list or a dict. (If it got a dict, it makes a list of one item out of it.) This should be fairly safe type-wise, too; other dict-like or list-like objects would probably be considered broken if they didn't have similar behavior.

Upvotes: 3

Brionius
Brionius

Reputation: 14098

Maybe I'm being naive, but how about something like

try:
    data.keys()
    print "Probs just a dictionary"    
except AttributeError:
    print "List o' dictionaries!"

Can you just go ahead and do whatever you were going to do anyways with the data, and decide whether it's a dict or list when something goes awry?

Upvotes: 1

abarnert
abarnert

Reputation: 365767

Really, there is no obvious pythonic way to do this, because it's an unreasonable input format, and the obvious pythonic way to do it is to fix the input…

But if you can't do that, then yes, you need to write an adapter (as close to the input edge as possible). The best way to do that depends on the actual data. If it really is either a dict, or a list of dicts, and nothing else is possible (e.g., you're calling json.loads on the results from some badly-written service that returns an object or an array of objects), then there's nothing wrong with isinstance.

If you want to make it a bit more general, you can use the appropriate ABCs. For example:

if isinstance(dict_or_list, collections.abc.Mapping):
    return [dict_or_list]
else:
    return dict_or_list

But unless you have some good reason to need this generality, you're just hiding the hacky workaround, when you're better off keeping it as visible as possible. If it's, e.g., coming out of json.loads from some remote server, handling a Mapping that isn't a dict is not useful, right?

(If you're using some third-party client library that just returns you "something dict-like" or "something list-like containing dict-like things", then yes, use ABCs. Or, if that library doesn't even support the proper ABCs, you can write code that tries a specific method like keys. But if that's an issue, you'll know the specific details you're working around, and can code and document appropriately.)

Upvotes: 6

Related Questions