11
\$\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.

asked Oct 16, 2016 at 20:12
\$\endgroup\$
4
  • \$\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\$ Commented Oct 16, 2016 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\$ Commented Oct 16, 2016 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\$ Commented Oct 16, 2016 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\$ Commented Oct 16, 2016 at 22:08

2 Answers 2

20
\$\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, []
    
Graipher
41.6k7 gold badges70 silver badges134 bronze badges
answered Oct 16, 2016 at 22:00
\$\endgroup\$
1
  • 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\$ Commented Oct 17, 2016 at 14:59
8
\$\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)
answered Oct 17, 2016 at 8:58
\$\endgroup\$
1
  • \$\begingroup\$ Good point about the name \$\endgroup\$ Commented Oct 18, 2016 at 3:50

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.