When working with a function that returns multiple values with a tuple, I will often find myself using the following idiom to unpack the results from inside a list comprehension.
fiz, buz = zip(*[f(x) for x in input])
Most of the time this works fine, but it throws a ValueError: need more than 0 values to unpack if input is empty. The two ways I can think of to get around this are
fiz = []
buz = []
for x in input:
a, b = f(x)
fiz.append(a)
buz.append(b)
and
if input:
fiz, buz = zip(*[f(x) for x in input])
else:
fiz, buz = [], []
but neither of these feels especially Pythonic—the former is overly verbose and the latter doesn't work if input is a generator rather than a list (in addition to requiring an if/else where I feel like one really shouldn't be needed).
Is there a good simple way to do this? I've mostly been working in Python 2.7 recently, but would also be interested in knowing any Python 3 solutions if they are different.
If f = lambda x: (x,x**2) then this works
x,y = zip(*map(f,input)) if len(input) else ((),())
If input=[], x=() and y=().
If input=[2], x=(2,) and y=(4,)
If input=[2,3], x=(2,3) and y=(4,9)
They're tuples (not lists), but thats thats pretty easy to change.
I would consider using collections.namedtuple() for this sort of thing. I believe the named tuples are deemed more pythonic, and should avoid the need for complicated list comprehensions and zipping / unpacking.
From the documentation:
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
33
>>> x, y = p # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y # fields also accessible by name
33
>>> p # readable __repr__ with a name=value style
Point(x=11, y=22)
You could use:
fiz = []
buz = []
results = [fiz, buz]
for x in input:
list(map(lambda res, val: res.append(val), results, f(x)))
print(results)
Note about list(map(...)): in Python3, map returns a generator, so we must use it if we want the lambda to be executed.list does it.
(adapted from my answer to Pythonic way to append output of function to several lists, where you could find other ideas.)
This question already has answers here:
How are tuples unpacked in for loops?
(8 answers)
Closed 7 years ago.
Let me start of with the generic I'm a beginner with Python introduction.
Hi, I'm a beginner with Python so please keep responses as closely aligned with plain English as possible :)
I keep running into these for loops where there are two iterating variables in a for loop. This is highly confusing to me because I just got my head wrapped around the basic concept of for loops. That is your iterating variable runs through one piece at a time through the for loop line by line (in most cases). So what then does two iterating variables do? I have some speculations but I'd like correct answers to put my thinking in the right direction.
Would someone type how the for loops would be read (in speaking terms) and explain what exactly is happening.
>>> elements = ('foo', 'bar', 'baz')
>>> for elem in elements:
... print elem
...
foo
bar
baz
>>> for count, elem in enumerate(elements):
... print count, elem
...
0 foo
1 bar
2 baz
In your code, you use the enumerate() function. You can imagine that it does the following:
>>> print enumerate("a","b","c")
>>> [(0,"a"),(1,"b"),(2,"c")]
(This isn't exactly correct, but it is close enough.)
enumerate() turns a list of elements into a list* of tuples, where each tuple consists of the index of the element and the element. Tuples are similar to lists.
*In fact it returns an iterator, not a list, but that shouldn't bother you too much.
But how do the two iteration variables work?
Let me illustrate this with a few examples.
In the first example, we iterate over each element in a list of integers.
simple_list = [1,2,3,4]
for x in simple_list:
print x
But we can also iterate over each element in a list of tuples of integers:
tuple_list = [(1,5),(2,6),(3,7),(4,8)]
for x, y in tuple_list:
print x, y
print x+y
Here, each element of the list is a tuple of two integers. When we write for tup in tuple_list, then tup is populated on each iteration with one tuple of integers from the list.
Now, because Python is awsome, we can "catch" the tuple of two integers into two different integer variables by writing (x,y) (or x,y, which is the same) in place of of tup
Does this make sense?
Analogously, we can iterate over 3-tuples:
tuple_list = [(1,5,10),(2,6,11),(3,7,12),(4,8,69)]
for x, y, z in tuple_list:
print x, y, z
print x+y-z
All of this works. Or should work (haven't run the code).
And of course,
tuple_list = [(1,5),(2,6),(3,7),(4,8)]
for tuple in tuple_list:
print tuple
also works.
Hello and thanks for looking at my question! I was recently introduced to generators and I read the about on 'The Python yield keyword explained'. From my understanding, yield is generated on the fly, cannot be indexed and will remember where it left off after the yield command has been executed (correct me if I'm wrong I'm still new to this).
My question is then why does:
from itertools import combinations
x = [['4A'],['5A','5B','5C'],['7A','7B']]
y = list()
for combination in x:
for i in range(1,len(combination)+1):
y.append(list(combinations(combination,i)))
print y # [[('4A',)], [('5A',), ('5B',), ('5C',)],
# [('5A', '5B'), ('5A', '5C'), ('5B', '5C')],
# [('5A', '5B', '5C')], [('7A',), ('7B',)], [('7A', '7B')]]
But this doesn't work:
from itertools import combinations
x = [['4A'],['5A','5B','5C'],['7A','7B']]
y = list()
for combination in x:
for i in range(1,len(combination)+1):
y.append((combinations(combination,i)))
print y
Since I am appending the combinations to y straight after it is yielded why is it that when it is appended in a list form it works, but when I do it normally, it doesn't?
When you call list(generator_function()), generator_function is iterated to exhaustion, and each element is stored in a list. So, these three operations are doing the same thing:
l = list(generator_function())
...
l = [x for x in generator_function()]
...
l = []
for x in generator_function():
l.append(x)
In your example, without including list(..), you're just appending the combinations generator object to the y. With list(...), you're iterating over the combinations generator to build a list object, and then appending that to y.
I know how to use both for loops and if statements on separate lines, such as:
>>> a = [2,3,4,5,6,7,8,9,0]
... xyz = [0,12,4,6,242,7,9]
... for x in xyz:
... if x in a:
... print(x)
0,4,6,7,9
And I know I can use a list comprehension to combine these when the statements are simple, such as:
print([x for x in xyz if x in a])
But what I can't find is a good example anywhere (to copy and learn from) demonstrating a complex set of commands (not just "print x") that occur following a combination of a for loop and some if statements. Something that I would expect looks like:
for x in xyz if x not in a:
print(x...)
Is this just not the way python is supposed to work?
You can use generator expressions like this:
gen = (x for x in xyz if x not in a)
for x in gen:
print(x)
As per The Zen of Python (if you are wondering whether your code is "Pythonic", that's the place to go):
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Flat is better than nested.
Readability counts.
The Pythonic way of getting the sorted intersection of two sets is:
>>> sorted(set(a).intersection(xyz))
[0, 4, 6, 7, 9]
Or those elements that are xyz but not in a:
>>> sorted(set(xyz).difference(a))
[12, 242]
But for a more complicated loop you may want to flatten it by iterating over a well-named generator expression and/or calling out to a well-named function. Trying to fit everything on one line is rarely "Pythonic".
Update following additional comments on your question and the accepted answer
I'm not sure what you are trying to do with enumerate, but if a is a dictionary, you probably want to use the keys, like this:
>>> a = {
... 2: 'Turtle Doves',
... 3: 'French Hens',
... 4: 'Colly Birds',
... 5: 'Gold Rings',
... 6: 'Geese-a-Laying',
... 7: 'Swans-a-Swimming',
... 8: 'Maids-a-Milking',
... 9: 'Ladies Dancing',
... 0: 'Camel Books',
... }
>>>
>>> xyz = [0, 12, 4, 6, 242, 7, 9]
>>>
>>> known_things = sorted(set(a.iterkeys()).intersection(xyz))
>>> unknown_things = sorted(set(xyz).difference(a.iterkeys()))
>>>
>>> for thing in known_things:
... print 'I know about', a[thing]
...
I know about Camel Books
I know about Colly Birds
I know about Geese-a-Laying
I know about Swans-a-Swimming
I know about Ladies Dancing
>>> print '...but...'
...but...
>>>
>>> for thing in unknown_things:
... print "I don't know what happened on the {0}th day of Christmas".format(thing)
...
I don't know what happened on the 12th day of Christmas
I don't know what happened on the 242th day of Christmas
The following is a simplification/one liner from the accepted answer:
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in (x for x in xyz if x not in a):
print(x)
12
242
Notice that the generator was kept inline. This was tested on python2.7 and python3.6 (notice the parens in the print ;) )
It is honestly cumbersome even so: the x is mentioned four times.
I personally think this is the prettiest version:
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in filter(lambda w: w in a, xyz):
print x
Edit
if you are very keen on avoiding to use lambda you can use partial function application and use the operator module (that provides functions of most operators).
https://docs.python.org/2/library/operator.html#module-operator
from operator import contains
from functools import partial
print(list(filter(partial(contains, a), xyz)))
I would probably use:
for x in xyz:
if x not in a:
print(x...)
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
set(a) & set(xyz)
set([0, 9, 4, 6, 7])
You can use generators too, if generator expressions become too involved or complex:
def gen():
for x in xyz:
if x in a:
yield x
for x in gen():
print x
I liked Alex's answer, because a filter is exactly an if applied to a list, so if you want to explore a subset of a list given a condition, this seems to be the most natural way
mylist = [1,2,3,4,5]
another_list = [2,3,4]
wanted = lambda x:x in another_list
for x in filter(wanted, mylist):
print(x)
this method is useful for the separation of concerns, if the condition function changes, the only code to fiddle with is the function itself
mylist = [1,2,3,4,5]
wanted = lambda x:(x**0.5) > 10**0.3
for x in filter(wanted, mylist):
print(x)
The generator method seems better when you don't want members of the list, but a modification of said members, which seems more fit to a generator
mylist = [1,2,3,4,5]
wanted = lambda x:(x**0.5) > 10**0.3
generator = (x**0.5 for x in mylist if wanted(x))
for x in generator:
print(x)
Also, filters work with generators, although in this case it isn't efficient
mylist = [1,2,3,4,5]
wanted = lambda x:(x**0.5) > 10**0.3
generator = (x**0.9 for x in mylist)
for x in filter(wanted, generator):
print(x)
But of course, it would still be nice to write like this:
mylist = [1,2,3,4,5]
wanted = lambda x:(x**0.5) > 10**0.3
# for x in filter(wanted, mylist):
for x in mylist if wanted(x):
print(x)
Use intersection or intersection_update
intersection :
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
ans = sorted(set(a).intersection(set(xyz)))
intersection_update:
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
b = set(a)
b.intersection_update(xyz)
then b is your answer
A simple way to find unique common elements of lists a and b:
a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
print(both)
based on the article here: https://towardsdatascience.com/a-comprehensive-hands-on-guide-to-transfer-learning-with-real-world-applications-in-deep-learning-212bf3b2f27a
I used the following code for the same reason and it worked just fine:
an_array = [x for x in xyz if x not in a]
This line is a part of the program! this means that XYZ is an array which is to be defined and assigned previously, and also the variable a
Using generator expressions (which is recommended in the selected answer) makes some difficulties because the result is not an array