k1ng_ambar1sh
k1ng_ambar1sh

Reputation: 51

why does variable's address change in python when the variable is assigned a different value?

I am using repl.it for python3 - Here is a sequence of assignments I did

  x34=20
   x34
=> 20
   hex(id(x34))
=> '0x7fdb5d318960'
   x34=30
   hex(id(x34))
=> '0x7fdb5d318aa0'
   hex(id(x34))
=> '0x7fdb5d318aa0'
   x34
=> 30
   hex(id(x34))
=> '0x7fdb5d318aa0'
   x34=20
   hex(id(x34))
=> '0x7fdb5d318960'

why does the address of the variable x34 change when I reassign it value 30?

Upvotes: 5

Views: 2011

Answers (2)

juanpa.arrivillaga
juanpa.arrivillaga

Reputation: 95908

The fundamental misunderstanding you have is that id's belong to Python objects, not the variables. So, any reassignment to a new object, would cause the id's to change:

>>> x = (1,2)
>>> hex(id(x))
'0x107526e48'
>>> x = (2, 4)
>>> hex(id(x))
'0x107526e88'

Note, every time you create a new object, this happens:

>>> x = (1, 2)
>>> hex(id(x))
'0x107526e48'
>>> x = (1, 2)
>>> hex(id(x))
'0x107526d48'
>>> x = (1, 2)
>>> hex(id(x))
'0x107526dc8'

Immutability or lack thereof has nothing to do with it. Note, tuple objects are immutable. However, there is a CPython implementation detail that small ints are cached. This is why you'll see the following behavior:

>>> x = 10
>>> hex(id(x))
'0x10714d920'
>>> x = 10
>>> hex(id(x))
'0x10714d920'
>>> x = 10
>>> hex(id(x))
'0x10714d920'

Essentially, in CPython, int objects between -5 and 256 are singletons. Just like None:

>>> x = None
>>> id(x)
4413491112
>>> x = None
>>> id(x)
4413491112

However, this wont work the same way with large ints, and works just like it does with tuples:

>>> x = 888
>>> hex(id(x))
'0x107550070'
>>> x = 888
>>> hex(id(x))
'0x10736aef0'
>>> x = 888
>>> hex(id(x))
'0x107550030'

Be careful, though, since id is only guaranteed unique for the lifetime of the object. So, sometimes, you'll see the same memory being returned from the heap, since the Python interpreter tries to be efficient with memory:

>>> x = 888
>>> hex(id(x))
'0x107550050'
>>> x = 888
>>> hex(id(x))
'0x10736aef0'
>>> x = 888
>>> hex(id(x))
'0x107550090'
>>> x = 888
>>> hex(id(x))
'0x10736aef0'
>>> x = 888
>>> hex(id(x))
'0x107550070'

Again, this might happen with any object:

>>> x = object()
>>> hex(id(x))
'0x1072db120'
>>> x = object()
>>> hex(id(x))
'0x1072db110'
>>> x = object()
>>> hex(id(x))
'0x1072db120'

And the value of the object doesn't matter,

>>> x = 888
>>> hex(id(x))
'0x107549fd0'
>>> x = 999
>>> hex(id(x))
'0x107550090'
>>> x = 777
>>> hex(id(x))
'0x107549fd0'

Indeed, neither does the type necessarily matter either:

>>> class A:
...    pass
...
>>> class B:
...    pass
...
>>> class C:
...    pass
...
>>> for i in range(12):
...     if i%3 == 1:
...         print(A())
...     elif i%3 == 2:
...         print(B())
...     else:
...         print(C())
...
<__main__.C object at 0x107535a58>
<__main__.A object at 0x107535a58>
<__main__.B object at 0x107535a58>
<__main__.C object at 0x107535a58>
<__main__.A object at 0x107535a58>
<__main__.B object at 0x107535a58>
<__main__.C object at 0x107535a58>
<__main__.A object at 0x107535a58>
<__main__.B object at 0x107535a58>
<__main__.C object at 0x107535a58>
<__main__.A object at 0x107535a58>
<__main__.B object at 0x107535a58>

This is all at the discretion of the interpreter implementation.

However, if you keep another reference to an object, the original will not die during reassignment, and that is guaranteed:

>>> hex(id(x))
'0x1072db140'
>>> y = x
>>> hex(id(y)) # guaranteed to be the same as x
'0x1072db140'
>>> x = object()
>>> hex(id(x)) # guaranteed to be new
'0x1072db130'

Upvotes: 8

user1785721
user1785721

Reputation:

Basically what you are doing with each assignment is creating a new object of type int, de-referencing the older one (20 in this case), and referencing with the variable (x34) to the new one (30).

Still be careful, because implementations could be tricky, look this:

>>> x34=20
>>> x34
20
>>> hex(id(x34))
'0x107d67f70'
>>> x34=30
>>> x34
30
>>> hex(id(x34))
'0x107d680b0'
>>> x35=30
>>> hex(id(x35))
'0x107d680b0'

As you can see, two different variables (x34 and x35) are pointing to the same object ( 30 in this case) when they "theoretically" shouldn't because the two 30 are different objects even they have the same values.

The Python interpreter is smart enough to realize that 30 is an immutable object, so it avoids wasting memory creating two 30 when with just one is enough because it (the interpreter) will never modify (due to the immutability rules) one object and such modification reflected in the other variable that point to the same object.

Upvotes: 2

Related Questions