Finding the type of indexing supported by an object?

Alex Martelli aleax at mac.com
Wed Aug 23 10:40:19 EDT 2006


Derek Peschel <dpeschel at eskimo.com> wrote:

> Here are two functions.
> 
> def invert_dict_to_lists(dict):
>     lists = {}
>     for key in dict:
>         value = dict[key]
>         if not value in lists:
>             lists[value] = [key]
>         else:
>             lists[value].append(key)
>     return lists
> 
> def invert_list_to_lists(list):
>     lists = {}
>     for key in range(len(list)):
>         value = list[key]
>         if not value in lists:
>              lists[value] = [key]
>         else:
>             lists[value].append(key)
>     return lists
> 
> They are the same except for the expression in "for key in ...".  Can they
> be combined into one function?  How can I determine if the argument is

They can easily be refactored, if that's what you mean:

def _invert_internal(container, keys):
    lists = {}
    for key in keys:
        value = container[key]
        if not value in lists:
             lists[value] = [key]
        else:
            lists[value].append(key)
    return lists

def invert_dict_to_lists(adict):
    return _invert_internals(adict, adict)

def invert_list_to_lists(alist):
    return _invert_internals(alist, xrange(len(alist)))

I've also performed a few other minor enhancements (never name things
dict or list because that hides the builtin types, use xrange vs range).
I have not changed the 4 lines in the if/else though I don't like them
(if not.../else is a very confusing construct -- at a minimum I'd
rephrase it as if/else swapping the bodies of the two clauses).

If you want to add a generic form accepting either lists or dicts you
need a try/except statement inside it, e.g.:

def invert_generic(container):
    try:
        container['zap']
    except TypeError:
        keys = xrange(len(container))
    except KeyError:
        keys = container
    else:
        keys = container
    return _invert_internal(container, keys)

Of course there are all sort of fragilities here -- e.g., something like
invert_dict_to_lists({23:[45]}) will crash and burn.  But that's quite
separate from the issue of distinguishing a dict from a list, which is
what the generic function is doing, showing how to handle exceptions for
the purpose.  Of course, there's no way to use a "totally" generic
container, because there are no real limits to the keys it may accept
(including infinite sets thereof, computing values in __getitem__).


Alex



More information about the Python-list mailing list