Recipe19.4.Unpacking a Few Items in a Multiple Assignment


Recipe 19.4. Unpacking a Few Items in a Multiple Assignment

Credit: Brett Cannon, Oren Tirosh, Alex Martelli

Problem

Python's multiple unpacking assignment is very handy when you are unpacking all the items from a sequence and binding each to a name. However, you often need to unpack (and bind to names) only some items from the "front" of a sequence, and bind another name to "the rest" of the sequence (the part you didn't unpack).

Solution

A generator provides an elegant solution to this problem:

def peel(iterable, arg_cnt=1):     """ Yield each of the first arg_cnt items of the iterable, then         finally an iterator for the rest of the iterable. """     iterator = iter(iterable)     for num in xrange(arg_cnt):         yield iterator.next( )     yield iterator if _ _name_ _ == '_ _main_ _':     t5 = range(1, 6)     a, b, c = peel(t5, 2)     print a, b, list(c) # emits: 1 2 [3, 4, 5]

Discussion

Python supports the handy idea of multiple unpacking assignment. Say that t5 is any sequence of five items. Then, you can code:

a, b, c, d, e = t5

to bind a name to each item of t5.

However, you often do not know (nor care) exactly how many items a certain sequence t holds: you want to bind (say) two names, one to each of the first two items, and a third name to "the rest" of t (this requirement does imply that t must hold at least two items). If you know that t is a "proper" sequence, with support for slicing, not just an arbitrary iterable, you can code:

a, b = t[:2] c = t[2:]

but this is nowhere as elegant or handy as the multiple unpacking assignment. Moreover, if you are not certain about the nature of t (i.e., if t can be any iterable, not necessarily supporting slice syntax), the task becomes more cumbersome:

c = iter(t5) a = c.next( ) b = c.next( )

Given these issues, the Python Development mailing list[1] once discussed a new syntax for generalized multiple unpacking assignment, such that:

[1] The Python Development mailing list is the list on which all discussion regarding the development of Python itself is held; see http://mail.python.org/pipermail/python-dev/2002-November/030380.html for this specific subject.

a, b, *c = t

would perform exactly this taskbind names a and b to the first two items of t and name c to "the rest".

I didn't like the idea of making the Python language bigger by adding this extra functionality to assignment statements, so I came up with this recipe's generator. This generator provides this functionality fully and without any need to add any new syntax to Python.

Just one caveat: you must make sure that you pass the arg_cnt argument properly. If you pass a wrong value for arg_cnt, or if the sequence you pass to peel is shorter than arg_cnt, you get an exception at runtime. But then, you also get a Python exception at runtime if you try to perform a multiple assignment and the number of names you have on the left of the = sign is not identical to the number of items of the sequence you have on the right. Therefore, this recipe isn't any different from normal, multiple unpacking assignment in this respect. If you think it is important to relax some parts of this requirement, see Recipe 19.5.

See Also

Language Reference and Python in a Nutshell about multiple unpacking assignments; Recipe 19.5.



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