I was wondering about variables assignments and why is this allowed:
a = 1, 2
a = b = 1
but this is not allowed:
a, b = 1
What is the logic behind?
Thank you
I'm going to assume you might be familiar with a language like C/C++, which is a statically-typed language. This means that the type of a variable must be declared when initialising a variable (eg you'd say int a;).
In C/C++, the syntax you are trying to do is valid syntax when doing int a, b = 1; (for example), because we're initialising two variables, a and b, to be integers, where the second one we're assigning a value 1.
However, Python is a dynamically typed language - the type of the variable does not need to be declared. Thus, when we do a, b = 1, we're actually using a feature Python has which is called "unpacking". Python is trying to unpack 1 to the variables a and b - but this is not possible since 1 is just a single piece of data - it's not a list or a tuple or whatever.
Because Python is dynamically typed, we can not just initiate a variable and not give it any value (like we do in C when we do int a;). When you do a, b = 1, it's trying to iterate through 1 and assign its contents to the variables a and b. Hence, the error TypeError: 'int' object is not iterable.
The left and right side are not symmetric. In
a = 1, 2
python does packing of the right-hand side arguments. The two comma-separated arguments create a tuple, so this is equivalent to a = (1, 2)
With
a, b = 1
python tries to do unpacking. It assigns the first value of the right-hand expression to a, and then tries to assign the second value to b. Since there is no second value, this will fail. It will treat the value 1 as iterable, so will give TypeError: int is not iterable.
You should write something like a, b = 1, 2.
In the first case python assumes a is a tuple of 1 and 2
>>> a = 1, 2
>>> a
(1, 2)
But a, b = 1 you want to give values to a and b, so there must be two values for them, but you're only providing one i.e 1. If you have a iterable of length of 2 then it would work.
>>> a, b = [6, 7]
>>> a
6
>>> b
7
In python when you give two number/strings with , python interpreter thinks its a tuple
a = 1, 2
in the above line you are creating tuple object called a
a, b = 1
In the above line left hand side syntax is for a tuple , so right side it expects tuple value
so
a,b = 1,1
works
Related
here is a simple (useless) function:
def f(x):
b = [x]
def g(a):
b[0] -= 1
return a - b[0]
return g
it works fine. let's change it a tiny bit:
def f(x):
b = x
def g(a):
b -= 1
return a - b
return g
Now it gives an error saying that b is undefined! Sure, it can be solved using nonlocal, but I'd like to know why does this happen in the first place? Why are mutables accessable and immutables aren't?
Technically, it has nothing to do with mutable or immutable (the language does not know whether a type is "mutable" or not). The difference here is because you are assigning to the variable in one case and just reading from it in the other case.
In your second example, the line b -= 1 is the same as b = b - 1. The fact you are assigning to the variable b makes a local variable named b, separate from the outside variable named b. Since the local variable b has not been assigned to when evaluating the right side of the assignment, reading from the local b there is an error.
In your first example, the line b[0] -= 1 is the same as b[0] = b[0] - 1. But that is not a variable assignment. That is just a list element access. It is just syntactic sugar for b.__setitem__(0, b.__getitem__(0) - 1). You are not assigning to the variable b here -- all you're doing is reading from the variable b two times (in order to call methods on the object it points to). Since you are only reading from b, it uses the external variable b.
If in the first example, with your mutable list, you did a variable assignment like you did in the second example, it would equally create a local variable, and the reading of that local variable before assignment would also not work:
def f(x):
b = [x]
def g(a):
b = [b[0] - 1]
return a - b[0]
return g
Consider this (uncontroversial) simple example:
allvars = []
a = 1
allvars.append(a)
b = 2
allvars.append(b)
c = 3
allvars.append(c)
d = 4
allvars.append(d)
e = 5
allvars.append(e)
for ix in allvars:
ix = ix + 1 # changes local variable ix, but not array elements
print(allvars) # [1, 2, 3, 4, 5]
for i, ix in enumerate(allvars):
allvars[i] = ix + 1 # changes array elements ...
print(allvars) # [2, 3, 4, 5, 6]
# ... but not original variables
print(a,b,c,d,e) # 1 2 3 4 5
Even if we had some variables "stored" into a Python list - changing that list did not change the original variables.
It is clear why this happens, if we recall that Python in fact stores pointers (as I read somewhere, "python has names, not variables"):
when we do a = 1; a points to the address of the int object 1
allvars[0], which is where we thought we stored a, also gets the address of the int object 1
In allvars[0] = allvars[0]+1, the final allvars[0] gets the address of the resulting int object, 2
however, that doesn't change the fact that a still points to the int object 1
The thing is, however, - I have a situation, where I have to manage a bunch of variables (like a, b ... above) separately; however, in the code, there are cases that would be more straightforward to handle, if I ran a loop over all these variables - but, the variables would have to be updated, because after the phase when the loops are useful, I have some remaining processing to be done on the variables (a, b etc) individually, where the updated values are expected.
So is there some (not too convoluted) way in Python, to put variables (or maybe, variable names) in a list/array, and then iterate over that array - and change the original variable (names)?
In terms of above example, I'd want to do something like this pseudocode:
...
for i, ix in enumerate(allvars):
change_originals("allvars[i] = ix + 1")
print(a,b,c,d,e) # this should be 2, 3, 4, 5, 6
Here you have created an array of primitives value. Primitives always copy whenever you use it. So mofication wont reflect on the original variable.
There are possible solution base on your requirement.
class Val:
def __init__(self, val = -1):
self._val = val
def get_val(self):
return self._val
# setter method
def set_val(self, x):
self._val = x
allvars = []
one = Val(1)
allvars.append(one)
print(allvars[0]._val)
one.set_val(2)
print(allvars[0]._val)
You can use a dictionary with key[1,2,3,4...]
You can create array of object
One way I can think of to do this would store the variable names as strings in a list, then use the exec function. This function always returns 0. It accepts a string argument and then executes that string as valid python code. So:
# Where allvars contains string names of variables
...
for i, ix in enumerate(allvars):
exec(f"{allvars[i]} = {ix} + 1")
Another way would use the locals() function, which gives you a dictionary with names and values of variables and any other names:
# using locals() function
# Make a list of variable names
allvars_strings = ['a','b','c','d','e']
# Slightly simpler
for i in allvars_strings:
locals()[i] = locals()[i] + 1
string = ''
print('\n\n')
for i in allvars_strings:
string += str(locals()[i]) + ', '
print(string[:-2])
I can assign a sequence like this in python:
a,b,c="ABC"
But I am unable to pass this sequence to a function as a parameter. i.e
def function2(a,b,c):
print a
print b
print c
function2("ABC")
The above statement is raising an error.
Can any one tell me the difference between assignment and argument passing in python?
The compiler sees a comma-separated list on the LHS and emits bytecode to iterate over the RHS for you. With the function call it sees a single value and so sends it as a single argument. You need to tell it to split the sequence explicitly:
>>> function2(*"ABC")
A
B
C
The function you created takes 3 parameters a, b, and c. But when you're calling the function you only provide one parameter.
To correctly call your function, you would need to do something like
function2("A","B","C")
and it would print
A
B
C
Since no one's mentioned this yet, the a, b, c = "ABC" assigns a = "A", b = "B" and c = "C" as a string also functions as an iterator of sorts, that is, you can loop over it, check for characters in it just like checking elements of a list etc etc.
It's similar to the code
a, b = 1, 2
Python interprets that as
(a, b) = (1, 2)
and the compiler detects 2 variables and so makes 2 assignments. When you pass 3 to a length 3 string, it works the same:
(a, b, c) = ("A", "B", "C")
However, as the others have said, in a function parameter passing, function2(a, b, c) must take 3 arguments, and "ABC" is only one object: a string, and so assigns a = "ABC" and then raises and error on b and c. To get the same behavior as a, b, c = ("A", "B", "C"), you parse it with a *, that is
function2(*"ABC") # is the same as function2("A", "B", "C")
The * parser works for any iterable: lists, (strings), dict (keys) etc
Functions can have optional parameters, specified as parameters with default argument values. Consider the function
def function2(a, b=None, c=None):
print a
print b
print c
Which of the following should function2("ABC") output?
ABC
None
None
or
A
B
C
Python opts for the first choice, instead of examining whether some of the parameters have default values, where an iterable value is treated as a single value for argument passing, and the special syntax
function2(*"ABC")
provides the second.
I'm kind of beginner in python. I was looking at one the types to make a fibonacci function,
def fib(n):
a=0
b=1
while a<n:
print a
a,b=b,a+b
and I saw the a,b=b,a+b declaration. So, I thought a=b and b=a+b were the same to a,b=a,b+a, so I changed the function for it to be like this:
def fib(n):
a=0
b=1
while a<n:
print a
a=b
b=a+b
and I thought it would be right, but when I executed the program, I got a different output. Can someone explain to me the difference between those two types of declaration?
Thanks, anyway.
b, a+b creates a tuple containing those two values. Then a, b = ... unpacks the tuple and assigns its values to the variables. In your code however you overwrite the value of a first, so the second line uses the new value.
a, b = b, a + b
is roughly equal to:
tmp = a
a = b
b = tmp + b
When Python executes
a,b = b, a+b
it evaluates the right-hand side first, then unpacks the tuple and assigns the values to a and b. Notice that a+b on the right-hand side is using the old values for a.
When Python executes
a=b
b=a+b
it evaluates b and assigns its value to a.
Then it evaluates a+b and assigns that value to b. Notice now that a+b is using the new value for a.
That syntax simultaneously assigns new values to a and b based on the current values. The reason it's not equivalent is that when you write the two separate statements, the second assignment uses the new value of a instead of the old value of a.
In the first example, a isn't updated to take the value of b until the entire line has been evaluated -- so b is actually a + b.
In you example, you've already set a to b, so the last line (b=a+b) could just as easily be b=b+b.
It's all in the order in which things are evaluated.
I'd like to know what is getting assigned to what in line 8.
# Iterators
class Fibs:
def __init__(self):
self.a = 0
self.b = 1
def next(self):
self.a, self.b = self.b, self.a+self.b # <--- here
return self.a
def __iter__(self):
return self
fibs = Fibs()
for f in fibs:
if f > 1000:
print f
break
The rest of the program I really don't need much explanation. I'm not sure what's getting assigned to what.
It's a multiple assignment roughly equivalent to this:
tmp = self.a
self.a = self.b
self.b = tmp + self.b
Or this pseudo-code:
a' = b
b' = a + b
As you can see the multiple assignment is much more concise than separate assignments and more closely resembles the pseudo-code example.
Almost that example is given in the Python documentation as an example of calculating Fibonacci numbers. One of the advantages of the multiple assignment is that the variables on the right hand side are evaluated before any of the assignments take place, saving the need for the temporary variable in this case.
It's a pair assignment, a shorthand of
t = self.a
self.a = self.b
self.b = t+self.b
just to use an one-liner instead that two assignments.. to be precise i think that the left operand of the assignment is considered a tuple of two elements, so you are like assigning to tuple (self.a, self,b) the value (self.b, self.a+self.b) which does the same thing as the three separate assignments written before without the need of a temporary variable. This because, while without using tuple the assignments are executed sequentially, in your example they are resolved at the same time, preserving the value of self.a in second assignment.
As stated in documentation:
Assignment of an object to a target list is recursively defined as follows.
If the target list is a single target: The object is assigned to that target.
If the target list is a comma-separated list of targets (your case): The object must be an iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets. (This rule is relaxed as of Python 1.5; in earlier versions, the object had to be a tuple. Since strings are sequences, an assignment like a, b = "xy" is now legal as long as the string has the right length.)
Without looking at the surrounding code, I'd say it's the heart of an algorithm for computing Fibonacci numbers.
It translates to the equivalent of:
a = b
b = a + b
...thereby computing the next number(s) in the sequence.
If you look at a sequence of numbers like
1 1 2 3 5 8 13 21 ...
and you let a and b be the last two numbers, then afterwards you'll have the next number in b and the former last number (b) in a.
The reason to use that strange notation is so as to accomplish both assignments at the same time. If they were done sequentially as in the 2 lines above, the value of a would be clobbered in the first line and you'd just be doubling b in the 2nd line.
Be aware that a paired assignment is not a "special feature" of Python. If you know a bit about Python, it's something you already know about but you may not know you know. When you put the following into the python console:
>>> 'a', 'b'
What you get in return is:
('a', 'b')
In other words, a tuple. In your example,
self.a, self.b = self.b, self.a+self.b
what you're really doing is:
(self.a, self.b) = (self.b, self.a+self.b)
Create a tuple that contains the value of self.b and the value of self.a+self.b. (The tuple on the right.)
Create a tuple that contains self.a and self.b. (The left-hand tuple.)
In order to create that left-hand tuple, create a new instance of self.a and self.b for that new tuple. Their old values don't matter anymore: they're in the temporary right-hand tuple.
Assign value 0 of the left tuple variable to value 0 of the right tuple.
Assign value 1 of the left tuple variable to value 1 of the right tuple.
Now that both variables of the left tuple are assigned, delete both tuples. The new variables remain with their new values.
So, for example, you can do:
>>> a, b = 1, 2
>>> a, b
(1, 2)
>>> a, b = b, a
>>> a, b
(2, 1)
There are still temporary variables involved under the hood, but you, the programmer, don't have to deal with them.