Python's Passing by References

Question:

Hello I am trying to understand how Python’s pass by reference works.
I have an example:

>>>a = 1
>>>b = 1
>>>id(a);id(b)
140522779858088
140522779858088

This makes perfect sense since a and b are both referencing the same value that they would have the identity. What I dont quite understand is how this example:

>>>a = 4.4
>>>b = 1.0+3.4
>>>id(a);id(b)
140522778796184
140522778796136

Is different from this example:

>>>a = 2
>>>b = 2 + 0
>>>id(a);id(b)
140522779858064
140522779858064

Is it because in the 3rd example the 0 int object is being viewed as “None” by the interpreter and is not being recognized as needing a different identity from the object which variable “a” is referencing(2)? Whereas in the 2nd example “b” is adding two different int objects and the interpreter is allocating memory for both of those objects to be added, which gives variable “a”, a different identity from variable “b”?

Asked By: Pulse

||

Answers:

As stated here, CPython caches integers from -5 to 256. So all variables within this range with the same value share the same id (I wouldn’t bet on that for future versions, though, but that’s the current implementation)

There’s no such thing for floats probably because there’s an “infinity” of possible values (well not infinite but big because of floating point), so the chance of computing the same value by different means is really low compared to integers.

>>> a=4.0
>>> b=4.0
>>> a is b
False

In your first example, the names a and b are both “referencing” the same object because of interning. The assignment statement resulted in an integer with the same id only because it has reused a preexisting object that happened to be hanging around in memory already. That’s not a reliable behavior of integers:

>>> a = 257
>>> b = 257
>>> id(a), id(b)
(30610608, 30610728)

As demonstrated above, if you pick a big enough integer then it will behave as the floats in your second example behaved. And interning small integers is optional in the Python language anyway, this happens to be a CPython implementation detail: it’s a performance optimization intended to avoid the overhead of creating a new object. We can speed things up by caching commonly used integer instances, at the cost of a higher memory footprint of the Python interpreter.

Don’t think about “reference” and “value” when dealing with Python, the model that works for C doesn’t really work well here. Instead think of “names” and “objects”.

names

The diagram above illustrates your third example. 2 is an object, a and b are names. We can have different names pointing at the same object. And objects can exist without any name.

Assigning a variable only attaches a nametag. And deleting a variable only removes a nametag. If you keep this idea in mind, then the Python object model will never surprise you again.

Answered By: wim

Python variables are always references to objects. These objects can be divided into mutable and immutable objects.

A mutable type can be modified without its id being modified, thus every variable that points to this object will get updated. However, an immutable object can not be modified this way, so Python generates a new object with the changes and reassigns the variable to point to this new object, thus the id of the variable before the change will not match the id of the variable after the change.

Integers and floats are immutable, so after a change they will point to a different object and thus have different ids.

The problem is that CPython “caches” some common integer values so that there are not multiple objects with the same value in order to save memory, and 2 is one of those cache-ed integers, so every time a variable points to the integer 2 it will have the same id (its value will be different for different python executions).

Answered By: Adirio