This question already has answers here:
How are tuples unpacked in for loops?
(8 answers)
Closed 9 years ago.
I'm new to python and trying to work my way through http://yuji.wordpress.com/2011/06/22/python-imaplib-imap-example-with-gmail/ which has the following line:
result, data = mail.uid('search', None, "ALL") # search and return uids instead
Could someone explain this line?
Thank you.
It means that the function you have called returns an iterable, and the index 0 of the iterable is assigned to x and the index 1 is assigned to y. This is called tuple unpacking.
Eg)
>>> def func(a,b):
... return b,a
...
>>> a = 5
>>> b = 7
>>> a,b = func(a,b)
>>> a
7
>>> b
5
>>> x = func(a,b)
>>> x
(5, 7)
Edit to show that returning multiple values, they are packed as tuple by default and then unpacked at the other end. Since there is only one variable x here, the tuple is assigned to x.
Simple function for swapping two variables(Just for an example) that answers your question
At least, as of python 2.7.x, the function will unpack a tuple of 2 arguments returned from a function. If it returns anything other than 2 arguments in the tuple, I believe it will throw an error if you try to unpack more than this. If it returns 3 arguments and you unpack 2, for example, you will get an exception.
For example:
def func(a):
return (a,a+1,a*2)
a,b,c = func(7)
print a,b
==> 7 8 # NOTE Values
a = func(3)
print a
==> (3, 4, 6) # NOTE: TUPLE
a,b = func(9)
print a,b
==> Exception - ValueError: too many values to unpack
This may be different in 3.0+.
The other answer, that "the function you have called returns an iterable" is a good one. That is what is happening in your specific example. This is what is called "unpacking" in python. The following are examples of unpacking and assignment related to your question:
>>> a,b = 1,2
>>> a
1
>>> b
2
>>> a,b,c = ['do', 're', 'mi']
>>> a
'do'
>>> b
're'
>>> c
'mi'
>>>
This is one of the pretty features of Python syntax. If I am not mistaken, it is also optimized - i.e. the fastest way to achieve the result.
Related
This question already has answers here:
The zip() function in Python 3
(2 answers)
Closed last month.
Created two lists in python and zipped them using zip function and saved them to a variable. Tried to print it to the console and was able to print successfully and when i tried again,empty list is printed.screenshot
Same with tuples.screenshot
Why i am able to retrieve values only once? Am in doing something wrong?
Python 3.7.1
The zip function is actually just an iterator -- an iterator can only be traversed once. If you want to be able to iterate over it multiple times, convert it to a list first.
a = [1,2,3]
b = [1,2,3]
c = list(zip(a, b))
Why is this?
Because those are similar to generators, so you can print first time, but when you try to print it again, it starts from the end, and no values left, see an example (clearer than yours):
>>> l=[1,2,3,4]
>>> it=iter(l) # generator basically (as in your question, zip, is also considered as a geneartor here)
>>> list(it)
[1, 2, 3, 4]
>>> list(it)
[]
>>>
So how to solve this issue?
Simply just replace this line:
zipped_list = zip(list1,list2)
With:
zipped_list = list(zip(list1,list2))
Then everything would work fine.
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.)
I am learning argument unpacking and found it an useful feature where we can directly retrieve the values inside a list without writing extra lines of code.
e.g.,
def simplePrint(x,y):
print x,y
myList = [3,4]
>>>simplePrint(*myList) #directly retrieve the values in myList ,storing the values in x & y and printing
3 4
but what if there are three, four,ten or hundred values in a list, for that we cannot pass that number of arguments in a function.So, for that I've tried this:
def simplePrint(*args):
print args
myList = [3,4,5]
>>>simplePrint(*myList)
(3,4,5)
which is a tuple
So my question is am I doing anything wrong? or is there a way to retrieve the values directly like the first example.
Thanks in advance :)
Using print as function (using __future__ - Future statement definitions)
>>> from __future__ import print_function
>>>
>>> def simplePrint(*args):
... print(*args)
...
>>> myList = [3,4,5]
>>> simplePrint(*myList)
3 4 5
or simply call print function:
>>> print(*myList)
3 4 5
If a function is written like this
def validate(input):
for a, b in input:
......
What is the syntactic form of input? I've tried a tuple but the function says there are "too many values to unpack".
In this case:
input is an iterable type (a list, tuple, etc.)
Its items are also iterable types. However, each item has exactly 2 items of its own.
I think the best way to explain this is with a demonstration:
>>> def validate(input):
... for a,b in input:
... print a,b
...
>>> data = [(0,1), (2,3), (4,5)]
>>> validate(data)
0 1
2 3
4 5
>>>
In the above code, notice how data is an iterable type (a list) and each of its items are iterable types (tuples in this case) that contain exactly 2 items.
for a,b in input expects input to be an iterable in which each element is a sub-iterable with exactly two items, e.g., ((1,2), ("foo", "bar"), ([], None)) but not (1, 2) or ((0,1), (2,3,4)).
This question already has an answer here:
Understanding mutability in Python [closed]
(1 answer)
Closed 9 years ago.
My question is more of an understanding question than a strict programming question.
I know that python variables are actually pointers, which means they don't actually store a value, but rather point to the place in memory where that value is stored.
What i can't figure out is how the following 2 cases differ:
>>> a = 3
>>> b = a
>>> a
3
>>> b
3
>>>b = 4
>>> b
4
>>> a
3
The new value assigned to 'b' does not change the value pointed to by 'a'. As oppose to:
>>> a = [1,2,3]
>>> b = a
>>> a
[1,2,3]
>>> b
[1,2,3]
>>> b.append(4)
>>> b
[1,2,3,4]
>>> a
[1,2,3,4]
The new value assigned to b changed the value pointed to by a
Calling b.append doesn't assign b to a new list. It still points to the same position in memory.
>>> b = [1,2,3]
>>> id(b)
36586568L
>>> b.append(4)
>>> id(b)
36586568L
Since it is the underlying data that changes, any other identifiers also pointing to that data will be affected.
This has been covered many times before. Short answer is that they aren't strictly pointers. They are more like labels. In your top case, b is re-labeled to 4 and thus changes. In your bottom case (with the array) b is not re-labeled, but only it's contents appended to. That's why arrays seem to act differently.
Python "variables" ("binding" would be a more appropriate term) are not pointers, they are key->value pairs in a namespace. How the namespace and lookup are implemented is, well, an implementation detail, but you can consider it's a hash table (Python's dict).
The new value assigned to b changed the value pointed to by a
Where do you see the assignement operator in b.append(4) ? You're not rebinding b, you are mutating it. It's still the same list object that is bound to both names a and b.