I want a function that normally takes in an argument of type X where X is either a scalar, a list, or a dict, and returns a list of X's with the same key values, based on other information.
def foo(info, k):
return [bar(item,k) for item in processInfo(info)]
def bar(item, keydata):
# pseudocode follows.
# What we want to do is return an output of parallel type to the input key k,
# using the key data to lookup information from the input item.
if keydata is a scalar:
return item[keydata]
elif keydata is a list:
return [item[k] for k in keydata]
elif keydata is a dict:
return dict((k,item[v]) for (k,v) in keydata.iteritems())
else:
raise ValueError('bar expects a scalar, list, or dict')
My question is, how can I dispatch between the three types?
edit: A string is to be interpreted as a scalar, not a list/iterable. Tuples are to be interpreted as iterable.
edit 2: I want duck typing, not strict typing.
But what's the difference between lists and dictionaries? A list is an ordered sequence of objects, whereas dictionaries are unordered sets. However, the main difference is that items in dictionaries are accessed via keys and not via their position.
One of the best ways to obtain the variable type is to use the type() function. Every value in Python has a datatype. Everything is an object in Python programming, and data types are classes, and variables are the instance (object) of these classes.
With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.
Use a dictionary when you have a set of unique keys that map to values. Use a list if you have an ordered collection of items. Use a set to store an unordered set of items.
You need to do things in the proper order since str
and dict
types are iterable.
from collections import Iterable, Mapping # in Python 3 use from collections.abc
def bar(item, keydata):
if isinstance(keydata, Mapping):
return {k: item[v] for (k,v) in keydata.iteritems()}
elif isinstance(keydata, Iterable) and not isinstance(keydata, str):
return [item[k] for k in keydata]
return item[keydata]
It depends on how strict you want to be with your input. The isinstance
approach forces you to specify the types to accept (I.e., no duck-typing). It works as long as your users are only passing in those classes or subtypes of those classes. You can also try to distinguish parameters by the methods they support. An example of this would be
Edit: added the special case for strings
if isinstance(keydata, basestring):
# special case to avoid considering strings as containers
# for python 3.x use str instead of basestring
return item[keydata]
try:
return dict((k,item[v]) for (k,v) in keydata.iteritems())
except AttributeError:
# it's not a dict-like
pass
try:
return [item[k] for k in keydata]
except TypeError:
# it's not iterable
return item[keydata]
The choice of control flow depends on how flexible you want to be, and also how you want yo deal with ambiguous cases. Eg, is a string considered a sequence of characters or a scalar?
Use the new fancy stuff :) by import collections
>>> isinstance([], collections.Sequence)
True
>>> isinstance({}, collections.Mapping)
True
You should also consider looking at the types module
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With