9
\$\begingroup\$

Python lacks dynamic unpacking. Example: You want to unpack a list, let's say coordinates, but don't know whether it contains 3 items or just 2.

x, y, z = [1,2,3] works only if len([x,y,z]) == len([1,2,3]).

x, y, z = [1,2] results in an error. You could add try and except blocks but that could be complicated.

The best option is z being None, as you can simply check using if n is None without any excessive try/except.

So expected result:

>>> x, y, z = unpack([1,2])
>>> print(x)
1
>>> print(y)
2
>>> print(z)
None

My code

def unpack(num, list):
    return_arr = [None] * num
    for i,elem in enumerate(list):
        return_arr[i] = elem
        if i+1 == num:
            return return_arr
    return return_arr

And usage examples:

a,b,c,d = unpack(4, [1,2,3])
print(a),
print(b),
print(c),
print(d),
print("\n")

e,f,g = unpack(3, [1,2,3])
print(e),
print(f),
print(g)

resulting in

1 2 3 None 

1 2 3

You basically have to specify the amount of variables you're unpacking the list to, since the function can't know that.

\$\endgroup\$
  • \$\begingroup\$ I'm wondering if it would be possible to create a class for iterables that will unpack dynamically (by magically having the right length or something). I've been trying to answer this, but I couldn't. \$\endgroup\$ – Oskar Skog Oct 16 '16 at 21:53
  • 1
    \$\begingroup\$ @OskarSkog Just replace the __iter__ method of your class by this unpack (granted you make it return an iterator). \$\endgroup\$ – 301_Moved_Permanently Oct 16 '16 at 22:02
  • \$\begingroup\$ @OskarSkog Nevermind, not really as you can't pass the number of variable you want to unpack into to the __iter__ method, but I would start digging in that direction anyway. \$\endgroup\$ – 301_Moved_Permanently Oct 16 '16 at 22:04
  • \$\begingroup\$ I just started thinking about it and realised that an infinite iterable would be easy to make but that would have too many value to unpack. I guess "Python lacks dynamic unpacking." really means what it means. \$\endgroup\$ – Oskar Skog Oct 16 '16 at 22:08
18
\$\begingroup\$
  1. It is generaly a bad idea to shadow a builtin (like list) by using a variable named after it.
  2. You can use slices and array extension to simplify a bit your algorithm:

    def unpack(n, lst):
        result = lst[:n]
        return result + [None] * (n - len(result))
    
  3. You can use the itertools module to improve memory management and allow for any iterable:

    import itertools
    
    
    def unpack(n, iterable):
        infinite = itertools.chain(iterable, itertools.repeat(None))
        return itertools.islice(infinite, n)
    
  4. Python 3 has an extended unpacking capability that is closer to your needs:

    >>> x, y, *z = [1, 2]
    >>> print(x, y, z)
    1, 2, []
    
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Awesome point, didn't realize I can just return a slice and each part of the rest as None. Thanks a lot. \$\endgroup\$ – Samuel Shifterovich Oct 17 '16 at 14:59
7
\$\begingroup\$
  1. The function has no docstring. What does it do? How do I call it? Are there any helpful examples you can present? The text in the post would make a good start.

  2. The name unpack is poorly chosen. I know that sequence unpacking is the use case that you have in mind, but the function does not actually unpack anything. What it does is to return a fixed-length prefix of a sequence, padding with None if necessary to make it up to the required length. So a name like prefix_pad_none would give a clearer indication of the behaviour. (Compare with the padnone recipe in the itertools documentation.)

  3. The pad value None should be a parameter to the function (with default value of None). That's because there are use cases in which you might want to pad a sequence with some other value. For example, zero, one, and NaN are common pad values in mathematical code.

Revised code:

from itertools import chain, islice, repeat

def prefix_pad(n, iterable, padvalue=None):
    """Return the first n elements of iterable, padded out with padvalue
    if iterable has fewer than n elements.

    """
    return islice(chain(iterable, repeat(padvalue)), n)
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Good point about the name \$\endgroup\$ – jamylak Oct 18 '16 at 3:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.