Recipe4.12.Building a Dict from a List of Alternating Keys and Values


Recipe 4.12. Building a Dict from a List of Alternating Keys and Values

Credit: Richard Philips, Raymond Hettinger

Problem

You want to build a dict from a list of alternating keys and values.

Solution

The built-in type dict offers many ways to build dictionaries, but not this one, so we need to code a function for the purpose. One way is to use the built-in function zip on extended slices:

def dictFromList(keysAndValues):     return dict(zip(keysAndValues[::2], keysAndValues[1::2]))

A more general approach, which works for any sequence or other iterable argument and not just for lists, is to "factor out" the task of getting a sequence of pairs from a flat sequence into a separate generator. This approach is not quite as concise as dictFromList, but it's faster as well as more general:

def pairwise(iterable):     itnext = iter(iterable).next     while True:         yield itnext( ), itnext( ) def dictFromSequence(seq):     return dict(pairwise(seq))

Defining pairwise also allows updating an existing dictionary with any sequence of alternating keys and valuesjust code, for example, mydict.update(pairwise(seq)).

Discussion

Both of the "factory functions" in this recipe use the same underlying way to construct a dictionary: each calls dict with an argument that is a sequence of (key, value) pairs. All the difference is in how the functions build the sequence of pairs to pass to dict.

dictFromList builds a list of such pairs by calling built-in function zip with two extended-form slices of the function's keysAndValues argumentone that gathers all items with even indices (meaning the items at index 0, 2, 4, . . .), the other that gathers all items with odd indices (starting at 1 and counting by 2 . . .). This approach is fine, but it works only when the argument named keysAndValues is an instance of a type or class that supports extended slicing, such as list, tuple or str. Also, this approach results in constructing several temporary lists in memory: if keysAndValues is a long sequence, all of this list construction activity can cost some performance.

dictFromSequence, on the other hand, delegates the task of building the sequence of pairs to the generator named pairwise. In turn, pairwise is coded to ensure that it can use any iterable at allnot just lists (or other sequences, such as tuples or strings), but also, for example, results of other generators, files, dictionaries, and so on. Moreover, pairwise yields pairs one at a time. It never constructs any long list in memory, an aspect that may improve performance if the input sequence is very long.

The implementation of pairwise is interesting. As its very first statement, pairwise binds local name itnext to the bound-method next of the iterator that it obtains by calling the built-in function iter on the iterable argument. This may seem a bit strange, but it's a good general technique in Python: if you start with an object, and all you need to do with that object is call one of its methods in a loop, you can extract the bound-method, assign it to a local name, and afterwards just call the local name as if it were a function. pairwise would work just as well if the next method was instead called in a way that may look more normal to programmers who are used to other languages:

def pairwise_slow(iterable):     it = iter(iterable)     while True:         yield it.next( ), it.next( )

However, this pairwise_slow variant isn't really any simpler than the pairwise generator shown in the Solution ("more familiar to people who don't know Python" is not a synonym of "simpler"!), and it is about 60% slower. Focusing on simplicity and clarity is one thing, and a very good oneindeed, a core principle of Python. Throwing performance to the winds, without getting any real advantage to compensate, is a completely different proposition and definitely not a practice that can be recommended in any language. So, while it is an excellent idea to focus on writing correct, clear, and simple code, it's also very advisable to learn and use Python's idioms that are most appropriate to your needs.

See Also

Recipe 19.7 for more general approaches to looping by sliding windows over an iterable. See the Python Reference Manual for more on extended slicing.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net