Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

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?

like image 439
k1ng_ambar1sh Avatar asked Jan 04 '23 13:01

k1ng_ambar1sh


1 Answers

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'
like image 52
juanpa.arrivillaga Avatar answered Jan 31 '23 18:01

juanpa.arrivillaga