1. Python's Object model
we know that in the world of Python, Everything is objects (object). According to the official Python document, the data Modeldescription, each Python object has 3 attributes: Identity, type, and value.
official documentation This summary of the object model is so important for us to understand the Python object, so this article extracts it as follows (to make the structure clearer, where the original document was processed in a segmented process):
1) Every object has an identity, a type and a value.
2) an object's identity never changes once it has been created; Think of it as the object ' s addressIn memory. The ' is ' operator compares the identity of the objects; The ID () function returns an integerrepresenting its identity (currently implemented as its address).
3) an object ' s type is also unchangeable. An object ' s type determines the operations , the objectsupports (e.g., "Does it has a length?") and also defines the possible values for objects of that type. thetype () function returns an object ' s type (which was an object itself).
4) The value of some objects can change. Objects whose value can change is said to be mutable; Objectswhose value is unchangeable once they was created is called immutable. (The value of an immutable containerobject, contains a reference to a Mutable object can change when the latter's value is changed; howeverthe container is still considered immutable, because the collection of objects it contains cannot beingchanged. So, immutability isn't strictly the same as has an unchangeable value, it's more subtle.)
5) An object ' s mutability are determined by its type; For instance, numbers, strings and tuples isimmutable, while dictionaries and lists is mutable.
To summarize:
1) Each Python object has 3 properties: Identity, type, and value
2) Once an object is created, its identity (which can be understood as the memory address of the object) is immutable. You can get the object by using Python's built-in function ID ()ID value, you can use the IS operator to compare whether two objects are the same object
3) The type of the created object cannot be changed, the type of the object determines the operation that can be used on the object, and also determines the values that the object might support
4) Value of some objects (such as list/dict) can be modified, such objects are referred to as mutable object, while others such asnumbers/strings/tuples) Once created, its value cannot be modified, so it is called immutable object
5) Whether the value of an object can be modified is determined by its type
2. Example describes the "modify" behavior of the value of immutable object
The above description indicates that the object of the numeric type is an immutable object. For a deeper understanding, consider the following sample code.
>>> x = 2.11>>> ID (x) 7223328>>> x + = 0.5>>> x2.61>>> ID (x) 7223376
In the code above, X + = 0.5 looks like a change to the value of an object named X.
But in fact, in Python's underlying implementation, X is just a pointer to the object's reference, or X is not an object of a numeric type.
What really happens in the above code is:
1) A float type object with a value of 2.11 is created with a reference count value of 1
2) x as a reference points to the object you just created, the reference count value of the object becomes 2
3) when "x + = 0.5" is executed, a float type object with a value of 2.61 is created (its initial reference count value is 1), and X points to the new object as a reference (meaning that the reference count value of the new object becomes 2, and the reference count value of the 1th object is reduced to 1 due to the "dereference" of X)
As you can see, the code above does not modify the value of the object named X, and the identifier x simply refers to the newly created object by re-referencing it, and we mistakenly assume that its value has been "modified".
3. An "odd" case
According to the above instructions, how do the following case understand?
>>> a = 20>>> B = 20>>> ID (a) 7151888>>> ID (b) 7151888>>> A is btrue
In the above code,
A and B should be references to different objects, and their ID values are unequal.。
But the fact that ID (a) = = ID (b) and "A is B" Output "True" indicates that the CPython interpreter is clearly not executing as we expected .
is there a bug in the interpreter implementation?
4. From the CPython realize the Pyintobject source code to reveal
In fact, the odd case not seen above is related to the optimizations made when CPython implements the Pyintobject type.
"Python Core Programming", section 4th. 5.2 Mentions:
An integer object is an immutable object, so Python caches it efficiently, which causes us to think that Python should create a new object without the illusion of creating a new object.
This is the underlying cause of the "odd" case we just met .
To confirm this, I looked at the source code for CPython v2.7 Open source on GitHub (cpython/objects/intobject.c) and see the following snippet:
#ifndef nsmallposints#define nsmallposints 257#endif#ifndef nsmallnegints#define nsmallnegints 5#endif#if Nsmallnegints + nsmallposints > 0/* References to small integers is saved in this array so the they can be shared . The integers that is saved is those in the range -nsmallnegints (inclusive) to nsmallposints (not inclusive). */stati C Pyintobject *small_ints[nsmallnegints + nsmallposints]; #endif
Visible
when the interpreter implements an int object, it does apply for a small_ints array to cache small integers, which can be seen from macro definitions and annotations, and the cached integer range is [ -5, 257]。
in this source file search "Small_ints" also can see, the array is used by 4 functions, the function name is: _pyint_init,Pyint_fromlong, Pyint_clearfreelist, Pyint_fini
where the latter two functions are related to the release of resources, we do not care here;In _pyint_init, a series of small int objects are constructed and deposited into the small_ints array, and in the Pyobject * Pyint_fromlong (long ival) function, if a small is constructed Int (that is, the incoming ival in the range of small int), the cached object in the Small_ints array is returned directly, and if the incoming ival is not within the small int access, the new object is created and its reference is returned.
At this point, we probably know the details of the CPython interpreter's implementation of the Int object, as well as the reason for the odd case we encountered.
In interactive mode, we've seen that the CPython interpreter does cache small integer objects, in fact, CPython when compiling the py script (compiled to bytecodes), and other optimizations that don't match the document description, StackOverflow this post weird The Integer Cache inside Python 2.6 explains this in detail and is worth reading.
In summary, the implementation details of the interpreter cannot be interfered with, but when we write the application, we make sure that the function logic does not rely on the " interpreter caches small integers " feature to avoid stepping into weird pits .
Resources
1. Python Doc:data Model
2. Section 4.5.2 of <core python programming>, 4th. 5.2 of Python core programming
3. Python Doc:plain Integer Objects-pyint_fromlong (Long ival)
4. GitHub Repo-cpython Source code:cpython/2.7/objects/intobject.c
5. Stackoverflow:weird Integer Cache inside Python 2.6
===================== EOF =======================
"Python notes" explore the implementation details of CPython for an int object from an "odd" case