Idiomatic way to unpack variable length list of maximum size n

2.2k views Asked by At

I'm reading a file and unpacking each line like this:

for line in filter(fh):
  a, b, c, d = line.split()

However, it's possible that line may have more or fewer columns than the variables I wish to unpack. In the case when there are fewer, I'd like to assign None to the dangling variables, and in the case where there are more, I'd like to ignore them. What's the idiomatic way to do this? I'm using python 2.7.

5

There are 5 answers

0
Hyperboreus On BEST ANSWER

Fix the length of the list, padding with None.

def fixLength(lst, length):
    return (lst + [None] * length)[:length]
6
Ashwini Chaudhary On

Something like this, works for any iterable/iterator. If you're always going to pass a list then you can remove the islice part.

from itertools import islice
def solve(seq, n):
    lis = list(islice(seq, n))
    return lis + [None]*(n - len(lis))
... 
>>> a, b, c, d = solve(range(2), 4)
>>> a, b, c, d
(0, 1, None, None)
>>> a, b, c, d = solve('qwe', 4)
>>> a, b, c, d
('q', 'w', 'e', None)
>>> a, b, c, d = solve(iter([1, 2, 3, 4, 5]), 4)
>>> a, b, c, d
(1, 2, 3, 4)
5
koffein On

In python 3 you can use this

a, b, c, d, *_unused_ = line.split() + [None]*4

Edit

For large strings I suggest to use maxsplit-argument for split (this argument also works in py2.7):

a, b, c, d, *_unused_ = line.split(None, 4) + [None]*4

Why 5? Otherwise the 4th element would consist the whole residual of the line.

Edit2 It is 4… It stops after 4 splits, not 4 elements

6
Marcin On

First of all, think about why you want to do this.

However, given that you want to (1) pad with None and (2) ignore extra variables, the code is easy:

a,b,c,d = (line.split() + [None]*4)[:4]

Obviously, the magic number has to be the same as the number of variables. This will extend what you have with the magic number, then trim back down to that length.

For an arbitrary iterable you can do:

import itertools

def padslice(seq,n):
    return itertools.islice(itertools.chain(seq,itertools.repeat(None)), n)

This is the same pad-and-slice with itertools.

0
gsamaras On

In Python 3, you can use itertools.zip_longest, like this:

from itertools import zip_longest

max_params = 4

lst = [1, 2, 3, 4]
a, b, c, d = next(zip(*zip_longest(lst, range(max_params))))
print(f'{a}, {b}, {c}, {d}') # 1, 2, 3, 4

lst = [1, 2, 3]
a, b, c, d = next(zip(*zip_longest(lst, range(max_params))))
print(f'{a}, {b}, {c}, {d}') # 1, 2, 3, None

For Python 2.x you can follow this answer.