Chapter 2 Using Python as a Calculator
2.8 Python Memory Management¶
When evaluating an arithmetic expression, Python first constructs an object in the memory for each literal in the expression, and then applies the operators one by one to obtain intermediate and final values. All the intermediate and final values are stored in the memory as objects. Python objects are one of the most important concepts in understanding the underlying mechanism of Python. Integers, floating point numbers and instances of many more types to be introduced in this book, are maintained in the memory as Python objects.
Fig. 2.1 illustrates how the memory changes when some arithmetic expressions are evaluated. After IDLE starts, the memory contains some default objects, which are not under concern at this stage, and therefore not shown in the figure. When the expression 3.0 is evaluated, a new float object is constructed in the memory. Python always creates a new object when it evaluates a literal. When the expression 3 + 5 ∗ 2 is evaluated, the integer constants 3, 5 and 2 are constructed in the memory, before the operators ∗ and + are executed in their precedence. When ∗ is executed, Python passes the values of the objects 5 and 2 to the CPU, together with the ∗ operator, and stores the result 10 as a new integer object in the memory. When + is executed, Python invokes the CPU addition operation with the values of the objects 3 and 10, storing the result 13 as a new object.
![Fig. 2.1 Example memory structure for expressions](../_images/fig2-1.jpeg)
Fig. 2.1 Example memory structure for expressions¶
Identifiers are names of objects in memory, used by Python to access the corresponding objects. Python associates identifiers to their corresponding values, or objects, by using a binding table, which is a lookup table.
Fig. 2.2 shows an example of the binding table, and how it changes as Python executes assignment statements. After IDLE starts, some default entries are put into the binding table, which are ignored in this figure. When x = 6 is executed, the expression 6 is first evaluated, resulting in the integer object 6 in the memory. Then Python adds an entry in the binding table, associating the name x with the object 6.
![Fig. 2.2 Example memory structure for assignments](../_images/fig2-2.jpeg)
Fig. 2.2 Example memory structure for assignments¶
When y = x ∗∗ 2 is executed, the value of the expression x ∗∗ 2 is first evaluated by evaluating x, and 2, and then calculating x ∗∗ 2. When Python evaluates the literal 2, it creates a new object in the memory; then when it evaluates the identifier x, it looks up the binding table for an entry named x, which is bound to the object 6. The final value of the expression x ∗∗ 2 is saved to a memory object 36, and bound to the identifier y.
When the statement x = 3 is executed next, the expression 3 is first evaluated, resulting in a new object 3 in the memory, which is bound to the name x in the binding table. The old association between the name x and the object 6 is deleted, since one name can be bound to only one object. Note that the assignment statement always binds a name in the binding table with an object in the memory. Like all other Python statements, the execution is rather mechanic. Given this fact, it is easy to understand the reason why the value of y does not change to 9 automatically when x = 3 is executed.
At this stage, the objects 2 and 6 are still in the memory, although they are not bound to any identifiers. As the number of statements increases, the memory can be filled with many such objects. They are no longer used, but still occupy memory space. Python has a garbage collector that periodically removes unused objects from the memory, so that more memory space can be available.
Python provides a special statement, the del statement, for deleting an identifier from the binding table.
As can be seen from the example above, the del statement begins with the del key word, followed by an identifier. It deletes the identifier from the binding table. As the second last command shows, Python reports a name error when the value of x is requested after the identifier x has been deleted from the binding table. After an identifier is deleted, the objected that it is bounded to is not deleted immediately. Instead, the garbage collector will remove it later when no other identifiers are bound to it.
Each Python object has a unique identifier, which is typically its memory address. Python provides a function, id, which takes a Python object as its input argument, and returns the identifier of the object. For example,
>>> x=12345
>>> id(x)
4535245720
>>> y=x
>>> id(y)
4535245720
>>> y=23456
>>> id(x)
4535245720
>>> id(y)
4535245672
>>> id(12345)
4535245984
When x is assigned to the value 12345, it is bound to a new object 12345 in the memory. When y is assigned to the value of x, it is bound to the same object 12345. Hence the identifies of x and y are the same. When y is reassigned to the value 23456, a new object 23456 is constructed in the memory, occupying a new memory address, and y is bound to his object. Hence the identifier of y changes, while the identifier of x remains the same. The last command shows the identifier of a new object, constructed by the evaluation of the expression 12345. It is different from that of x , because every time a literal is evaluated, a new object is constructed in the memory. Here is another example.
>>> x=12345
>>> y=x
>>> id(x)
4462893976
>>> id(y)
4462893976
>>> x=12345
>>> id(x)
4462894072
>>> id(y)
4462893976
When x is assigned to the value 12345 the second time, the right hand side of the assignment statement is evaluated first, which leads to a new object having the value 12345 in the memory. x is bound to this new object, while y remains the same. Although the values of x and y are the same, their memory addresses, or identifies are different, because they are bound to two different objects.
Note that the observations above may not hold for small numbers (e.g. 3 instead of 12345). This is because to avoid frequent construction of new objects, Python constructs at initialization a set of frequently used objects, including small integers, so that they are reused rather than constructed afresh when their literals are evaluated.
>>> x=10
>>> y=10
>>> id(x)
140621696282752
>>> id(y)
140621696282752
In the example above, y is assigned the value 10 after x is assigned the value 10. In each case, no new object is created when the literal 10 is evaluated, because an object with the value 10 has been created in the memory location 140621696282752 to represent all objects with this value.
In summary, in addition to a value, a Python object also has an identifier and a type. Once constructed, the identifier and type of an object cannot be changed. For number objects, the value also cannot be changed after the object is constructed.
© Copyright 2024 GS Ng.