Reputation: 2619
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 dict
s.
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
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
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
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
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
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