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.
Related
In the below code sample I would expect both a and b to be deleted at the end of each loop.
What happens is that a is deleted but not b. Why is that? And how can I make sure that b is also deleted at the end of each loop?
class bag:
a = 5
b = []
def funcie(self):
self.a = self.a + 1
self.b.append(1)
for i in range(5):
inst = bag()
inst.funcie()
print(inst.a)
print(inst.b)
# del inst
output:
6
[1]
6
[1, 1]
6
[1, 1, 1]
6
[1, 1, 1, 1]
6
[1, 1, 1, 1, 1]
EDIT: So this post explains why the b list keeps growing in each loop i.e. I should have declared the b list in the __init(self)__ function.
However it doesn't explain why the a variable gets overwritten at the end of each loop whilst the b variable doesn't.
bag.a (a class attribute) is not being overridden, it's being shadowed by the instance's a (an instance attribute) for inst specifically.
Python's general rule is that reading will read from outer/shadowed scopes if there is no inner/shadowing scope hiding it. An inner/shadowing scope is created by assignment, not merely mutation (which is just reading from the variable then asking it to mutate itself). There are some subtle distinctions between how scopes and attributes work, so I'm going to focus on attributes only (since that's what you asked about).
When you do self.a = self.a + 1 on a new instance of bag, the self.a you read comes from the class attribute, but on writing to self.a, you create a new shadowing instance attribute. The class attribute (bag.a == 5) still exists, and is unmodified, but from the instance's point of view, it only sees the instance attribute (inst.a == 6). If you added print(bag.a), you'd see if never changed.
By contrast, self.b.append(1) reads the class attribute and asks it to modify itself in place, which changes object bound to the class attribute itself.
If you want to modify a class attribute without creating an instance attribute, you have to operate on the class itself. For example, you could change:
self.a = self.a + 1
to:
type(self).a = self.a + 1 # Or type(self).a = type(self).a + 1
or more simply use += to avoid repeating more complex stuff:
type(self).a += 1
Numbers differ from the list in that Python uses their value and stores the result of computations in a new place. So the result of a = a+1 is stored in a different location in memory each time. Lists remain in the same memory location and are updated in place. The following code:
class bag:
a = 5
b = []
def funcie(self):
self.a += 1
self.b.append(1)
inst = bag()
print("bag memory ids: ", id(bag.a), id(bag.b))
print("inst memory ids: ", id(inst.a), id(inst.b))
inst.funcie()
print("ids after 1x funcie:", id(inst.a), id(inst.b))
inst.funcie()
print("ids after 2x funcie:", id(inst.a), id(inst.b))
has this output, where you see the id of inst.a changing, while inst.b retains the same id:
bag memory ids: 9789120 139707042123328
inst memory ids: 9789120 139707042123328
ids after 1x funcie: 9789152 139707042123328
ids after 2x funcie: 9789184 139707042123328
So the updated value of a is stored in a different location, without altering the original value.
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
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
I have wrote following code in python
class check(object):
def __init__(self):
self.a = [1,2,3,4]
self.b = 5
appending(self.a, self.b)
print "a", self.a
print "b", self.b
def appending(a,b):
a.append(5)
b +=1
If now I run check() function, I got output as following:
a [1,2,3,4,5] [1,2,3,4,5]
b 5 5
So, I have following question
Why it is that list(a) is got updated but not int(b)?
It is related that I am modifying a but i am creating new object when I add 1 in b, in short, it is difference due to immutable or mutable data types.
I have define self.a and self.b in object, i have define a,b in function, then why I can write print a and print b in object, get same output as self.a and self.b
self.b is a name for an integer 5. The integer is an immutable object.
When you appear to mutate b you actually create a new object 6 and assign b as a name for it. This in no way affects the object 5 which self.b is a name for.
Since a is a list, it's passed by reference to the method and any changed done to it within the method, will be done directly to the a.
Variable b is an integer, therefore it's passed by value and a copy of the variable is created to be used within the method. Any change will be visible only within the body of the method, but not to the "outside world".
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.