3

suppose I have a dictionary

kwargs = {'key1': 1, 
          'key2': 2,
          'key3list': [1,2,3,4]}

Where one of the values of any of the keys could be a list of integers, while the others would just be any object, in this case, an integer. I want to, in a single line (or in a couple at most) , put all the values of the keys into one tuple, unpacking all possible lists. Notice that by how the dictionary kwargs is constructed, keys having a list will have the key ending with 'list'.

I came up with the following:

a = tuple(
[kwargs[key] if not key.endswith('list') else *kwargs[key] for key in kwargs.keys()]
)

However I get the error that I cannot unpack *kwargs[key] here..

How could I fix this?

Euler_Salter
  • 3,271
  • 8
  • 33
  • 74
  • Maybe duplicate [of](https://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python). It is at least related to that question – Quickbeam2k1 May 03 '18 at 09:39

2 Answers2

3

If you don't have to use list comprehension, a generator could be used:

def flat(l):
    for k, v in l.items():
        if type(v) == list:
            for x in v:  
                yield x
        else:
            yield v

kwargs = {'key1': 1,
          'key2': 2,
          'key3list': [1,2,3,4]}

print(tuple(flat(kwargs)))

Output:

(1, 2, 1, 2, 3, 4)

Note that the dict has no order so the resulting tuple can change based on the order that items() returns the dictionary items.

3

It can be done using a nested list comp, but it's not pretty. One way is to wrap non-list items into lists, and then unpack the resulting 2D list.

kwargs = {
    'key1': 1,
    'key2': 2,
    'key3list': [1, 2, 3, 4],
}

a = [u for v in 
        [val if key.endswith('list') else [val] for key, val in kwargs.items()] 
    for u in v]
print(a)

output

[1, 2, 1, 2, 3, 4]

I seriously recommend using traditional looping techniques here, don't try to do it all in a list comp. Eg,

a = []
for key, val in kwargs.items():
    if key.endswith('list'):
        a.extend(val)
    else:
        a.append(val)

print(a)

The above code can be written as

a = []
for key, val in kwargs.items():
    meth = a.extend if key.endswith('list') else a.append
    meth(val)

But even that is a bit too "clever", and less readable than the previous version.


We can make the code a little more general by ignoring the keys and testing if the current object is a list or not:

a = []
for val in kwargs.values():
    if isinstance(val, list):
        a.extend(val)
    else:
        a.append(val)

As Lutz Horn notes, this unpacking strategy is a little dangerous if the order of the output list is important, since traditionally in Python a plain dict doesn't necessarily retain insertion order. However in Python 3.6+, dicts do retain order, so this technique is safe, but it still makes me a little uncomfortable. ;)

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182