简体   繁体   中英

Multiple assignation with list comprehension

I am trying to know if I can assign multiple lists with one single list comprehension. I somehow can't get my mind around the syntax.

So, instead of doing...

xs = [item.km for item in data]
ys = [item.price for item in data]

I'd like to do...

xs, ys = [km, price for km, price in data]
# or...
xs, ys = [item.km, item.price for item in data]

But this throws me a syntax error, and I can't seem to find the error.

Even if it seemed obvious, the data is as follow...

for elem in data:
    print elem
# outputs (this is a namedtuple) :
# mileage(km=22899.0, price=7990.0)
# mileage(km=48235.0, price=6900.0)
# mileage(km=54000.0, price=7990.0)
# mileage(km=60949.0, price=7490.0)
...

如果我正确理解您的结构,则需要带星号的zip()来转置数据:

xs, ys = zip(*[(km, price) for km, price in data]) 

A single list comprehension produces a single list. You've tried to assign a list of the structure [(a,b), (a,b), (a,b)] to two variables using multiple assignment, and this doesn't work because the number of entries doesn't match. You could instead produce lists of the pair components:

kms = [item.km for item in data]
prices = [item.price for item in data]

But this does process the list data twice. If it's really important to avoid this, we could build the two lists in parallel, but that still isn't a single comprehension:

kms, prices = [], []
for item in data:
    kms.append(item.km)
    prices.append(item.price)

You could achieve a lower load on the memory manager by preallocating the lists:

kms, prices = [None]*len(data), [None]*len(data)
for i,item in enumerate(data):
    kms[i]=item.km
    prices[i]=item.price

But most likely you'd be better off processing the data in a joint manner using something like numpy or pandas.

It's possible to use a fold to produce two lists with input from one comprehension, but it's both complex and inefficient in common Python implementations.

Simple answer is, like others have pointed already: a list comprehension produces a single list .

You could, however, as @Chris_Rands suggested, transpose your data using zip . I would tweak it and use a generator to make it a little faster.

xs, ys = zip(*((item.km, item.price) for item in data))

The problem is that the above will iterate multiple times over the data set in order to produce what you expect. Using a plain for iteration will perform far better, as Yann's answer states (that answer has an even better alternative):

xs, ys = [], []
for item in data:
    xs.append(item.km)
    ys.append(item.price)

Sometimes we need to sacrifice some things in favour of performance. And in this case:

  • zip is an great tool, but it isn't exactly meant for that. That snippet isn't easily readable for people unaware of the use case, and is also slow (O(2*n^2) or maybe O(n^2) with a generator, I haven't tested).
  • Using a plain for will make your code really faster (O(n)), which may really matter depending on the size of data .

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