This question already has answers here:
Python Multiple Assignment Statements In One Line
(5 answers)
How does Python's comma operator work during assignment?
(3 answers)
Closed 3 years ago.
Can anybody explain what's going on here? Why does this happen?
>>> b = "1984"
>>> a = b, c = "AB"
>>> print(a, b, c)
'AB', 'A', 'B'
This behavior really blows my mind.
Found this here
Assignment is a statement; it's defined to assign the far right side to the various targets from left to right. The rather dry language grammar description is:
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
So for example:
a = b = 1
assigns 1 to a, then assigns it again to b, roughly the same as if you did:
__specialtmp = 1
a = __specialtmp
b = __specialtmp
where __specialtmp is an unnamed temporary storage location (on CPython, it's just loaded on the top of the program stack, then duplicated into two references, then each reference is popped off for assignment).
This just adds iterable unpacking to the mix; expanding your code the same way, it would look like:
__specialtmp = "AB"
a = __specialtmp # Assigns original value to a
b, c = __specialtmp # Unpacks string as iterable of its characters, assigning "A" to b, and "B" to c
This won't always work mind you; if the thing being unpacked is an iterator, and you assign to the unpacked names first, the iterator will be exhausted and nothing useful will be available for the second assignment:
b, c = [*a] = iter("AB")
This unpacks "A" to b, and "B" to c, but when it gets to a, which in plain [*a] = iter("AB") would become ["A", "B"] (the star syntax capturing "remaining" values to a list), in this case, the iterator gets exhausted populating b and c and a gets nothing (the empty list, []).
Point is, while this trick works, I wouldn't recommend it in general. Initializing multiple names to the same immutable value is fine, but it's liable to bite you otherwise.
Such a cool question! Makes a lot of fun! :) Can be used at interviews :)
Ok, here we are
>>> b = "1984"
>>> a = b, c = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>> a = (b, c) = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>>
In python for multiple assignments, you can omit (...) and it looks like python parses this line similar to 2 lines
a = "AB"
b, c = "AB" # which is equal to (b, c) = "AB"
Some more examples
>>> a = b, c = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>> a = (b, c) = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>> a = "AB"
>>> b, c = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>>
It works using lists a well :)
>>> a = [b, c] = 'AB'
>>> print((a,b,c))
('AB', 'A', 'B')
>>>
Some more examples:
https://www.geeksforgeeks.org/unpacking-a-tuple-in-python/
https://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/
Let's make this a little simpler. Let's look at the following case
>>> b = 1
>>> b, c = (0, 2)
>>> print(b, c)
0, 2
Is it surprising that b is 0 and not 1? It shouldn't be since we assigning b to 0 and c to 2 when calling b, c = (0, 2) thanks to tuple unpacking.
Now to address the other part of the gotcha, let's take this example
>>> b = 1
>>> a = b = 0
>>> print (b)
0
Is it again surprising that b is 0 and not 1? Again, it shouldn't be since when calling a = b = 0, we've assigned both a and b to 0 with multiple assignment.
So coming back to the gotcha, the a = b, c = "AB" is just a combination of these two behaviors. b, c = "AB" will unpack "A" to b and "B" to c, and we're also assigning "AB" to a. While it looks like we're assigning a = b, we're really just doing the following two lines
>>> b = "1984"
>>> b, c = "AB"
>>> a = "AB"
Hopefully this breaks down where the tuple unpacking is happening and where the assignment is happening, and that it's not as confusing of a gotcha as it might look.
Related
I saw this Python snippet on Twitter and was quite confused by the output:
>>> a, b = a[b] = {}, 5
>>> a
{5: ({...}, 5)}
What is going on here?
From the Assignment statements documentation:
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
You have two assignment target lists; a, b, and a[b], the value {}, 5 is assigned to those two targets from left to right.
First the {}, 5 tuple is unpacked to a, b. You now have a = {} and b = 5. Note that {} is mutable.
Next you assign the same dictionary and integer to a[b], where a evaluates to the dictionary, and b evaluates to 5, so you are setting the key 5 in the dictionary to the tuple ({}, 5) creating a circular reference. The {...} thus refers to the same object that a is already referencing.
Because assignment takes place from left to right, you can break this down to:
a, b = {}, 5
a[b] = a, b
so a[b][0] is the same object as a:
>>> a, b = {}, 5
>>> a[b] = a, b
>>> a
{5: ({...}, 5)}
>>> a[b][0] is a
True
This question already has answers here:
How can I select a variable by (string) name?
(5 answers)
Closed 4 years ago.
I would like to run a function with a list parameter :
param = ['a', 'b', 'c']
myFunction(param)
But a, b and c must be dynamic so I would like to remove the quotes to get this :
param = [a, b, c]
myFunction(param)
How can I do that ?
PS : I tried the solutions of this question but it didn't work Removing quotation marks from list items
If I understood correctly, you want to actually call myFunction with parameters a, b and c (assuming these are actually being defined earlier).
One way of doing this is:
a, b, c = 1, 2, 3
param = ['a', 'b', 'c']
def myFunction(x, y, z):
return x + y + z
myFunction(*[globals()[s] for s in param])
# returns: 6
EDIT:
As people have suggested, a safe way of doing this would be through globals() (or locals() using a slightly different construct). Otherwise, you could use eval() which is more powerful, but also potentially less safe.
'str''s cannot be converted to variable names, however if you do have th variables and your goal is to pass each variable to a function this is possible
def addition(x):
return x + 1
a = 1 b = 2 c = 3
param = [a, b, c]
print([addition(i) for i in param])
# [2, 3, 4]
The entire list could be passed as well like so
def addition(x):
return sum(x)
a = 1
b = 2
c = 3
param = [a, b, c]
print(addition(param))
# 6
I saw this Python snippet on Twitter and was quite confused by the output:
>>> a, b = a[b] = {}, 5
>>> a
{5: ({...}, 5)}
What is going on here?
From the Assignment statements documentation:
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
You have two assignment target lists; a, b, and a[b], the value {}, 5 is assigned to those two targets from left to right.
First the {}, 5 tuple is unpacked to a, b. You now have a = {} and b = 5. Note that {} is mutable.
Next you assign the same dictionary and integer to a[b], where a evaluates to the dictionary, and b evaluates to 5, so you are setting the key 5 in the dictionary to the tuple ({}, 5) creating a circular reference. The {...} thus refers to the same object that a is already referencing.
Because assignment takes place from left to right, you can break this down to:
a, b = {}, 5
a[b] = a, b
so a[b][0] is the same object as a:
>>> a, b = {}, 5
>>> a[b] = a, b
>>> a
{5: ({...}, 5)}
>>> a[b][0] is a
True
I am curious to know if there is a "pythonic" way to assign the values in a list to elements? To be clearer, I am asking for something like this:
myList = [3, 5, 7, 2]
a, b, c, d = something(myList)
So that:
a = 3
b = 5
c = 7
d = 2
I am looking for any other, better option than doing this manually:
a = myList[0]
b = myList[1]
c = myList[2]
d = myList[3]
Simply type it out:
>>> a,b,c,d = [1,2,3,4]
>>> a
1
>>> b
2
>>> c
3
>>> d
4
Python employs assignment unpacking when you have an iterable being assigned to multiple variables like above.
In Python3.x this has been extended, as you can also unpack to a number of variables that is less than the length of the iterable using the star operator:
>>> a,b,*c = [1,2,3,4]
>>> a
1
>>> b
2
>>> c
[3, 4]
Totally agree with NDevox's answer
a,b,c,d = [1,2,3,4]
I think it is also worth to mention that if you only need part of the list e.g only the second and last element from the list, you could do
_, a, _, b = [1,2,3,4]
a, b, c, d = myList
is what you want.
Basically, the function returns a tuple, which is similar to a list - because it is an iterable.
This works with all iterables btw. And you need to know the length of the iterable when using it.
One trick is to use the walrus operator in Python 3.8 so that you still have the my_list variable. And make it a one-line operation.
>>> my_list = [a:=3, b:=5, c:=7, d:=2]
>>> a
3
>>> b
5
>>> c
7
>>> d
2
>>> my_list
[3, 5, 7, 2]
PS: Using camelCase (myList) is not pythonic too.
What's new in Python 3.8 : https://docs.python.org/3/whatsnew/3.8.html
You can also use a dictionary. This was if you have more elements in a list, you don't have to waste time hardcoding that.
import string
arr = [1,2,3,4,5,6,7,8,9,10]
var = {let:num for num,let in zip(arr,string.ascii_lowercase)}
Now we can access variables in this dictionary like so.
var['a']
I would like to unpack the return of a function into :
a first variable always set up by the first returned value
a second variable to store any exceeded returned value
To do so, I have this code working under python3.x. How could I make it works with python 2.x (python2.6 at least) ?
a,*b = ['a','b','c']
Edit: This would also work with :
a,*b = ['a']
There is no straight forward way to do this in Python 2.7, instead you can create a new list without the first element and first element alone and unpack them into respective variables.
data = ['a','b','c']
a, b = data[0], data[1:]
print a, b
Output
a ['b', 'c']
This solution will still work, even if the RHS has only one element
data = ['a']
a, b = data[0], data[1:]
print a, b
Output
a []
Use slicing:
>>> lis = ['a','b','c']
>>> a, b = lis[0], lis[1:]
>>> a
'a'
>>> b
['b', 'c']