Memory allocation of python object - python

I have the following code:
a = 2
b = a
a = a + 2
print (a)
print (b)
My question is why does b print out as 2 and not 4? If you assign a to b, doesn't b always reference to the memory of a?
Thanks

Because you have reassigned x to a new location by x = x + 1, and it's not about immutability.
For checking:
x = 2
y = x
>>> id(x), id(y)
(26726784, 26726784) # same
Then, change this:
x = x+1
>>> id(x), id(y)
(26726760, 26726784) # not same, because x reassigned to a new reference location.
If you do the same for list, which are mutable, you'll have the same result:
x=[5]
y=x
>>> id(x), id(y)
(139890260976056, 139890260976056)
on assignment:
x = x + [5]
>>> id(x), id(y)
(139890260094344, 139890260976056) # not same, id reallocated on assignment
>>> x, y
([5, 5], [5])
You'll see mutable behavior on list by using x.append(5), where both x and y change as you're modifying the object itself. But in this case, it's not the mutability of the object that are causing the difference. It's the assignment x=something else that changed the reference.
An interesting property about int in python is that smaller ones are pre-allocated. For instance, if you do:
x = 5
y = x
x = 5
id(x) == id(y) # True
The id will be the same, however, if you do:
x = 5000000000000
y = x
x = 5000000000000 # same as previous.
id(x) == id(y) # False
This is due to small integers having pre-allocated location while large ones don't. As such reassignment for large integer will find a different location instead.
This validates #juanpa.arrivillaga's point that this is due to assignment, not immutability.

The variable gets re-allocated to a new address when you re-assign a:
>>> a = 2
>>> b = a
# get memory address for a and b
>>> id(a)
4357961072
>>> id(b)
4357961072
# they are the same
# now reassign
>>> a = a + 2
# id of a has changed
>>> id(a)
4357961136
# id of b has not
>>> id(b)
4357961072

Not all python objects are mutable, here's list of which are.

Related

The difference between '=' and 'append' in list in this example?

I know list is a kind of changeable collection of data object ,but why the output is different.
I thought they should be the same.
a = [1]
b = a
b = [1,2]
print(a)
output:
[1]
a = [1]
b = a
b.append(2)
print(a)
output:
[1,2]
In the first example you are overwriting:
a=[1]
b=a # b=[1] and b=a
b=[1,2] # b=[1,2] but not a
in the second example you apply a function built into lists:
a=[1]
b=a # b=[1] and b=a
b.append(1) # applies append to b which is a so a.append is done
a = [1]
b = a
b = [1,2]
print(a)
When you do this, the value for b is reassigned therefore losing the connection with a.
a = [1]
b = a
b.append(2)
print(a)
But here, appending to b means the same list in the memory is appended a value. Since a and b still refer to the same memory, printing a results the same as b since they two are just two different aliases for the list.

Confused about assinging list items to vars in python

Say I have a list:
L = [1,2,3]
and I assigned L[0] to a variable a
a = L[0]
then if I change a, it won't affect L.
a = a + 1
print L # [1,2,3] not affected
Why is this happening? isn't python passing everything around with references? I thought that a is pointing to L[0]
The problem is that a and L[0] are references to an immutable object, so changing any one of them won't affect the other references:
>>> L = [1, 2, [3]]
>>> a = L[0]
>>> a = a + 1
a now points to a new object, while L[0] still points to the same object.
>>> a, L[0]
(2, 1)
Now in this case b and L[2] are references to a mutable object(list), any in-place operation on them will affect all the references:
>>> b = L[2]
>>> b.append(4) #list.append is an in-place operation
>>> b, L[2]
([3, 4], [3, 4])
>>> b = b + [5] #mutable object, but not an in-place operation
>>> b #b is assigned to new list object
[3, 4, 5]
>>> L[2] #L[2] is unchanged
[3, 4]
L[0] is a name, and when you create the list L, you assign an object to that name, the integer 1. a is also a name, and when you assign a as in a = L[0], you make a to point to the same object that L[0] points to.
But when you later do a = a + 1, this is another assignment. You are not modifying the object that a points to -- the = sign can't do that. You are creating a new object, the integer 2, and assigning that to a.
So in the end, you have two objects in memory; one is referred to by L[0] and the other is referred to by a.
Integers are immutable, which means that there is no possible way to change the properties of the objects in this example; however, that's not salient in this example exactly, because even if the object was mutable it wouldn't change the fact that you're doing assignment (with the = sign). In a case where the object in question was mutable, you could theoretically change the properties of the object when it is still referenced by L[0] and a, instead of doing any additional assignment with = as you are doing. At that point, you would see the properties change regardless of which name you used to inspect the object.
Since L[0] in your case is immutable, changing a doesn't affect the value of L[0]. When you change a, the new object is created and a starts to pointing to it.
See what happens if L[0] is of a mutable type:
>>> L = [[1],2,3]
>>> a = L[0]
>>> a.append(2)
>>> L
[[1, 2], 2, 3]
In this case a and L[0] both point to the same object.
Also see Raymond Hettinger's answer in the relevant thread.
Change the assignment to:
a = L
then when you change L as:
L[0] += 1
you will see that a also changes. This is the reference magic.

python objects : digging deeper

Hi all I know what this code does:
1.] My first problem
x = 4
y = x
But what about this. Why same addresses even for this case?
x = 4
y = 4
id(x)
12345678
id(y)
12345678
2.] My second problem
x = 42
y = x
x = x + 1
print x # Prints 43
print y # Prints 42
x = [1, 2, 3]
y = x
x[0] = 4
print x # Prints [4, 2, 3]
print y # Prints [4, 2, 3]
But why is it that in the case of list, both x & y got mutated together by the command x[0] = 4.
What is so different with lists, in such a behavior?
What makes them behave like this?
And most importantly what is the benefit of such a behavior?
why cant list, variables, tuples have all the properties of each other?
Small integers are cached in CPython, that's why their id's are same.
Integers are immutable, so modifying(i.e assigning it to a new object) one will not affect the other references.
Lists are mutable, so modifying any reference(in-place modification) to a mutable object will affect other references as well.
Small strings can have same id's too:
Integer example:
>>> x = 100
>>> y = x
>>> id(x),id(y)
(165193204, 165193204)
>>> x += 1
>>> id(x), id(y) #`x` now points to a new object, `y` still points to the old object
(165193192, 165193204)
Your first problem can be answered with memory optimization. If you dig a little further, for example:
x = 4
y = 4
# Both (may) have the same id
x += 1
# id of x and y will now be different
The second is reference semantics. Since just about everything in Python is really a reference, setting x = y is simply saying point y to the same thing that x points at. If you actually want a completely separate copy, you'll need to say so:
x = [1, 2, 3, 4]
y = x[:]
x[0] = 5
y
>>> [1, 2, 3, 4]

is searching a python list for an empty list inaccurate?

Why can b list be found in some_list even though it's not in some_list?
>>> a = []
>>> b = []
>>> a is b
False
>>> some_list = [0, 1, 2, 3, a]
>>> some_list.index(b)
4
.index returns 4th position because it compares a and b, two empty lists. They are equal. If you do a is b, it compares the memory addresses of objects, and they are different, that's why this expression evaluates to False.
In [1]: a = []
In [2]: b = []
In [3]: a == b
Out[3]: True
Python's list.index searches tests for equality, not identity. a and b are not identical, but equal:
>>> a = []
>>> b = []
>>> a == b
True
Define __eq__ on your objects a,b if you want a different definition of equality, for example:
def __eq__(self, other):
return self is other
def __hash__(self):
return 0 # Insert a more sensible, object-specific hash function here
They are different:
a is b
is true only if they are both bound to the same object. However, index only checks if the values are equal.
To put a C example, 3 == 3 but
int a = 3;
int b = 3;
&a == &b // this is false
Try to execute the a == b. The '==' operator checks, if the values of variables are the same, and so does the index function. The is function comapres the variables id().
This might be helpful:
>>> a = []
>>> b = []
>>> id(a)
4299650400
>>> id(b)
4299730096
>>> a == b
True
>>> a is b
False
Also note this one:
>>> some_list = [0, 1, 2, 3, a]
>>> some_list.index(b)
4
>>> some_list[4] == b
True
>>> some_list[4] is b
False

Python references

Can someone explain why the example with integers results in different values for x and y and the example with the list results in x and y being the same object?
x = 42
y = x
x = x + 1
print x # 43
print y # 42
x = [ 1, 2, 3 ]
y = x
x[0] = 4
print x # [4, 2, 3]
print y # [4, 2, 3]
x is y # True
The best explanation I ever read is here:
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
Because integers are immutable, while list are mutable. You can see from the syntax. In x = x + 1 you are actually assigning a new value to x (it is alone on the LHS). In x[0] = 4, you're calling the index operator on the list and giving it a parameter - it's actually equivalent to x.__setitem__(0, 4), which is obviously changing the original object, not creating a new one.
If you do y = x, y and x are the reference to the same object. But integers are immutable and when you do x + 1, the new integer is created:
>>> x = 1
>>> id(x)
135720760
>>> x += 1
>>> id(x)
135720748
>>> x -= 1
>>> id(x)
135720760
When you have a mutable object (e.g. list, classes defined by yourself), x is changed whenever y is changed, because they point to a single object.
That's because when you have a list or a tuple in python you create a reference to an object.
When you say that y = x you reference to the same object with y as x does.
So when you edit the object of x y changes with it.
As the previous answers said the code you wrote assigns the same object to different names such aliases.
If you want to assign a copy of the original list to the new variable (object actually)
use this solution:
>>> x=[1,2,3]
>>> y=x[:] #this makes a new list
>>> x
[1, 2, 3]
>>> y
[1, 2, 3]
>>> x[0]=4
>>> x
[4, 2, 3]
>>> y
[1, 2, 3]

Categories

Resources