Recipe19.5.Automatically Unpacking the Needed Number of Items


Recipe 19.5. Automatically Unpacking the Needed Number of Items

Credit: Sami Hangaslammi, Peter Cogolo

Problem

You want to unpack (and bind to names) some items from the "front" of a sequence and bind another name to "the rest" of the sequence (the part you didn't unpack). You want to obtain the number of items to unpack automatically, based on how many names are on the left of the = sign in a multiple unpacking assignment.

Solution

The previous approach in Recipe 19.4 is clean and elegant, but you have to "manually" pass the number of items to unpack. If you're willing to stoop to a little black magic, peering into stack frames and bytecodes, you may be able to bypass that requirement:

import inspect, opcode def how_many_unpacked( ):     f = inspect.currentframe( ).f_back.f_back     if ord(f.f_code.co_code[f.f_lasti]) == opcode.opmap['UNPACK_SEQUENCE']:         return ord(f.f_code.co_code[f.f_lasti+1])     raise ValueError, "Must be a generator on RHS of a multiple assignment!" def unpack(iterable):     iterator = iter(iterable)     for num in xrange(how_many_unpacked( )-1):         yield iterator.next( )     yield iterator if _ _name_ _ == '_ _main_ _':     t5 = range(1, 6)     a, b, c = unpack(t5)     print a, b, list(c)

Discussion

While arguably spiffy, this recipe is a bit fragile, as you could well expect from a function relying on introspection on bytecode: while the recipe works in Python 2.3 and 2.4, any future release of Python might easily generate bytecode for a multiple unpacking assignment in a somewhat different way, and thus break the recipe.

Moreover, as presented, the recipe relies on how_many_unpacked being called specifically from a generator; if you call it from an ordinary function, it does not work, since in that case the UNPACK_SEQUENCE bytecode in the caller's caller happens to fall at offset f.f_lasti+3 instead of f.f_lasti.

For example, the following code doesn't work with the recipe's Solution because enumfunc is an ordinary function, not a generator:

def enumfunc( ):     return xrange(how_many_unpacked( )) a, b, c, d, e = enumfunc( )

However, the following code does work:

def enumgen( ):     for x in xrange(how_many_unpacked( )): yield x a, b, c, d, e = enumgen( )

because enumgen is a generator.

In other words, this recipe is a hackarguably a neat hack (to the point that one of the editors of this Cookbook successfully lobbied the "other" two and managed to obtain the recipe's inclusion in this volume), but, nevertheless, a hack. Therefore, you probably do not want to use this approach in "production code", meaning code that must stay around for a long time and will be maintained across future versions of Python.

Nevertheless, you could make how_many_unpacked work in both contexts by making it a little bit more complicated:

def how_many_unpacked( ):     f = inspect.currentframe( ).f_back.f_back     bytecode = f.f_code.co_code     ups_code = opcode.opmap['UNPACK_SEQUENCE']     if ord(bytecode[f.f_lasti]) == ups_code:         return ord(bytecode[f.f_lasti+1])     elif ord(bytecode[f.f_lasti+3]) == ups_code:         return ord(bytecode[f.f_lasti+4])     else:         raise ValueError, "Must be on the RHS of a multiple assignment!"

With this more complicated variant, how_many_unpacked would work when called from either a generator or an ordinary function. However, I recommend sticking with the simpler version presented in this recipe's Solution, and calling how_many_unpacked only from the given unpack generator, or a few other specific generators.

Even such a limited use can be considered debatable, since most Pythonistas prefer clarity and simplicity to the risky kind of "convenience" that can be obtained by such shortcuts. After all, this recipe's only advantage, in comparison to Recipe 19.4, is that you save yourself the trouble of passing to unpack the number of items required, which is nice, but clearly, not all that crucial."

See Also

Recipe 19.4; Language Reference and Python in a Nutshell about multiple unpacking assignments; Library Reference and Python in a Nutshell about library modules inspect and opcode.



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