multiple variables in list comprehension?

我与影子孤独终老i 提交于 2021-01-20 15:30:09

问题


I want to create a list of lists from a list of multi-field strings and wonder if it is possible to do so in a comprehension.

Input:

inputs = ["1, foo, bar", "2, tom, jerry"]

Desired output:

[[1, "foo", "bar"], [2, "tom", "jerry"]]

Splitting the string in a comprehension is easy:

>>> [s.split(",") for s in inputs]
[['1', ' foo', ' bar'], ['2', ' tom', ' jerry']]

But I'm having trouble figuring out how to access the columns after the string has been split inside the comprehension, because it would seem to require a variable assignment. The following are not valid Python, but illustrate what I'm looking for:

[[int(x), y.strip(), z.strip() for x,y,z = s.split(",")] for s in inputs]
    or
[[int(v[0]), v[1].strip(), v[2].strip() for v = s.split(",")] for s in inputs]

Is there a way to assign variables inside a comprehension so that the output can be composed of functions of the variables? A loop is trivial, but generating a list by transforming inputs sure seems like a "comprehension-ish" task.

outputs = []
for s in inputs:
    x,y,z = s.split(",")
    outputs.append([int(x), y.strip(), z.strip()])

回答1:


You can do this with two for clauses in your list comprehension. The first iterates over the items in the list. The second iterates over a single-item list containing the list derived from splitting the string (which is needed so we can unpack this into three separate variables).

[[int(x), y.strip(), z.strip()] for s in inputs for (x, y, z) in [s.split(",")]]

The for clauses go in a somewhat counterintuitive order, but it matches the way you'd write it as nested for loops.

Jon Sharpe's use of a nested comprehension (generator expression, actually) is similar and probably clearer. The use of multiple for clauses always seems confusing to me; mainly I wanted to see if I could make use of it here.




回答2:


Thanks for all the suggestions - it's great to see the variety of possible techniques, as well as a counterexample to the zen of Python's "There should be one — and preferably only one — obvious way to do it."

All 4 solutions are equally beautiful, so it's a bit unfair to have to give the coveted green check to only one of them. I agree with the recommendations that #1 is the cleanest and best approach. #2 is also straightforward to understand, but having to use a lambda inside a comprehension seems a bit off. #3 is nice in creating an iterator with map but gets a tiny demerit for needing the extra step of iterating over it. #4 is cool for pointing out that nested for's are possible -- if I can remember that they go in "first, second" order instead of "inner, outer". Since #1 is not in the form of an answer, #4 gets the check for most surprising.

Thanks again to all.

inputs = ["1, foo, bar", "2,tom,  jerry"]

outputs1 = [[int(x), y.strip(), z.strip()] for x,y,z in (s.split(',') for s in inputs)]
print("1:", outputs1)       # jonrsharpe

outputs2 = [(lambda x, y, z: [int(x), y.strip(), z.strip()])(*s.split(",")) for s in inputs]
print("2:", outputs2)       # yper

outputs3 = [z for z in map(lambda x: [int(x[0]), x[1].strip(), x[2].strip()],[s.split(",") for s in inputs])]
print("3:", outputs3)       # user2314737

outputs4 = [[int(x), y.strip(), z.strip()] for s in inputs for (x, y, z) in [s.split(",")]]
print("4:", outputs4)       # kindall

Results:

1: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
2: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
3: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
4: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]



回答3:


You can use map with a list comprehension

def clean(x):
   return [int(x[0]), x[1].strip(), x[2].strip()]

map(clean,[s.split(",") for s in inputs])
# Output: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]

with a lambda function:

map(lambda x: [int(x[0]), x[1].strip(), x[2].strip()],[s.split(",") for s in inputs])



回答4:


If you really want to do it in one line, you can do something like this, although it's not the clearest code (first line is the code, second line is the output).

>>> [(lambda x, y, z: [int(x), y.strip(), z.strip()])(*s.split(",")) for s in inputs]
[[1, 'foo', 'bar'], [2, 'tom', 'jerry']]

Or this.

>>> [(lambda x: [int(x[0]), x[1].strip(), x[2].strip()])(s.split(",")) for s in inputs]
[[1, 'foo', 'bar'], [2, 'tom', 'jerry']

Edit: See jonrsharpe's comment for the best answer IMHO.




回答5:


Another possible solution

>>> inputs = [[1, "foo", "bar"], [2, "tom", "jerry"]]
>>> list_of_dicts = [{"var{}".format(k): v for k, v in enumerate(s, start=1)} for s in inputs]
>>> list_of_dicts[0]['var1']
1
>>> list_of_dicts[0]['var2']
'foo'


来源:https://stackoverflow.com/questions/40351085/multiple-variables-in-list-comprehension

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!