Integer assignment (int) in Python

Question:

Let's say you have the following code:

a = 32
b = a

print(a is b)

How to make b become a reference to a new value in memory, and not to the value that a refers to, i.e. how to make the operation print(a is b) print False ?

Answer:

b = a makes b name refer to the same object as a name.

The a is b operation checks if both names point to the same object, so a is b will always always be True after a = b in Python.

If you want the name b refer to an object other than a , then just assign any other object, for example, b = None for the example in the question, or you can delete the name del b altogether (if you try to use it, an exception will be thrown after that).


If you want a and b be equal ( a == b == 32 ), but different objects ( a is not b ), then the answer may depend on the specific Python implementation.

For example, in CPython, small integers are cached, so 32 is (16*2) is (31+1) … that is, there is only one object that represents 32 in CPython. Integers are immutable in Python, so arithmetic won't change whether the same object for 32 returned each time or different.

Note that you should not use the is operator to compare numbers. Since different objects can have the same value (be equal), for example 1000 == (999+1) , but 1000 is not (999+1) (in this case, (999+1) returns a new object different from the object, created for 1000 constants). is can be used to compare to a singleton — an object that only exists, such as None . Python does not promise that there is only one instance of objects representing numbers, so use == to compare integers.


There are several solutions to the literal problem ( a == b == 32 and a is not b ) if viewed as a puzzle (not for practical use).

To bypass the caching of small numbers, you can create an int derived class:

>>> a = 32
>>> class Int(int):
...     pass
...     
>>> b = Int(a)
>>> a == b == 32 and a is not b
True

Or use float or any other type that can be compared with integers:

>>> b = 32.
>>> a == b == 32 and a is not b
True

Finally, if you want type(a) == type(b) == int , where int is the built-in PyLongObject type, then it is possible to do the evil thing and change the value of the well-known constant 42 :

>>> import ctypes
>>> import sys
>>> a = 32
>>> b = 42
>>> (ctypes.c_ubyte * sys.getsizeof(b)).from_address(id(b))[-4] = a
>>> a == b == 32 and a is not b
True
>>> type(a) == type(b) == int
True
>>> (32 + 10) == 32
True
  • id(b) returns the address of an object in memory representing 42 in CPython
  • sys.getsizeof(b) returns the size of object b in bytes
  • (ctypes.c_ubyte * sys.getsizeof(b)) specifies the type of byte array that b object can hold ( unsigned char[28] )
  • .from_address() creates an array from memory, starting at the address where the b object is located
  • arr[-4] = a sets the fourth byte from the end to 32 . Which has the effect of changing the constant 42 (due to how _longobject is represented in memory (the only / digit changes) , in particular, it says that the python3 command uses PYLONG_BITS_IN_DIGIT == 30 , that sys.int_info confirms the sys.int_info order of my machine, which sys.byteorder also confirms).

The code was inspired by the article: "… How to Get Rid of a Colleague Writing in Python" .

Scroll to Top