简体   繁体   中英

Python cannot use unpacking in list comprehension with ternary operator

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?

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.

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. ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM