In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)05/31 Report--
This article introduces the relevant knowledge of "life cycle source code analysis of Python objects". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Think about:
When we enter this statement, how does Python create this object internally?
A = 1.0
After the object has been used, how can the timing of destruction be determined?
Next, let's take a basic type, float, as an example to analyze the behavior of objects throughout the lifecycle from creation to destruction.
1 C API
Python is written in C, provides API to allow users to interact with it from the C environment, and Python uses a lot of these API internally. C API is divided into two categories: generic API and special API.
Generic API: independent of type, belongs to the abstract object layer. The parameter of this type of API is PyObject *, that is, it can handle any type of object. Take PyObject_Print as an example:
/ / print floating-point objects PyObject * fo = PyFloat_FromDouble; PyObject_Print (fo, stdout, 0); / / print integer objects PyObject * lo = PyLong_FromLong (100); PyObject_Print (lo, stdout, 0)
Special API: related to type and belongs to specific object layer. This kind of API can only act on certain types of objects.
2 creation of objects 2.1 two ways to create objects
Objects are generally created within Python in two ways:
Through C API, it is mostly used for built-in types
Take floating-point type as an example. Python provides PyFloat_FromDouble internally, which is a special C API. Inside this interface, memory is allocated for PyFloatObject structure variables and related fields are initialized:
PyObject * PyFloat_FromDouble (double fval) {PyFloatObject* op = free_list; if (op! = NULL) {free_list = (PyFloatObject*) Py_TYPE (op); numfree--;} else {op = (PyFloatObject*) PyObject_MALLOC (sizeof (PyFloatObject)); if (! op) return PyErr_NoMemory () } / * Inline PyObject_New * / (void) PyObject_INIT (op, & PyFloat_Type); op- > ob_fval = fval; return (PyObject *) op;}
Through type objects, it is mostly used for custom types.
For custom types, Python cannot provide C API in advance, in which case the instance object can only be created from the metadata contained in the type object (how much memory is allocated, how to initialize, and so on).
Creating instance objects from type objects is a more general process, and for built-in types, in addition to creating object accidents through C API, it can also be created through type objects. Taking floating-point types as an example, we create an instance object f through the type object float:
F: float = float ('3.123') 2.2 create instance objects from type objects
Think about it: since we can create instance objects from type objects, there should be interfaces in the type objects.
The tp_call field was found in PyType_Type:
PyTypeObject PyType_Type = {PyVarObject_HEAD_INIT (& PyType_Type, 0) "type", / * tp_name * / sizeof (PyHeapTypeObject), / * tp_basicsize * / sizeof (PyMemberDef), / * tp_itemsize * / (destructor) type_dealloc / * tp_dealloc * / / (ternaryfunc) type_call, / * tp_call * / /...}
Therefore, float ('3.123') is equivalent at the C level to:
PyFloat_Type.ob_type.tp_call (& PyFloat_Type, args. Kwargs)
Here you can think about why PyFloat_Type.ob_type-- is because we create a floating-point object through the type object float in float ('3.14'), and the general method of the object is managed by its corresponding type, and the type of natural float is type, so what we are looking for is the tp_call field of type.
C source code of type_call function: (only part of it is listed)
Static PyObject * type_call (PyTypeObject * type, PyObject * args, PyObject * kwds) {PyObject * obj; / /. Obj = type- > tp_new (type, args, kwds); obj = _ Py_CheckFunctionResult ((PyObject*) type, obj, NULL); if (obj = = NULL) return NULL; / /. Type = Py_TYPE (obj); if (type- > tp_init! = NULL) {int res = type- > tp_init (obj, args, kwds); if (res
< 0) { assert(PyErr_Occurred()); Py_DECREF(obj); obj = NULL; } else { assert(!PyErr_Occurred()); } } return obj;} 其中有两个关键的步骤:(这两个步骤大家应该是很熟悉的) 调用类型对象的tp_new函数指针,用于申请内存; 如果类型对象的tp_init函数指针不为空,则会对对象进行初始化。 总结:(以float为例) 调用float,Python最终执行的是其类型对象type的tp_call指针指向的type_call函数。 type_call函数调用float的tp_new函数为实例对象分配内存空间。 type_call函数必要时进一步调用tp_init函数对实例对象进行初始化。 图示如下: 3 对象的多态性 通过类型对象创建实例对象,最后会落实到调用type_call函数,其中保存具体对象时,使用的是PyObject *obj,并没有通过一个具体的对象(例如PyFloatObject)来保存。这样做的好处是:可以实现更抽象的上层逻辑,而不用关心对象的实际类型和实现细节。(记得当初从C语言的面向过程向Java中的面向对象过度的时候,应该就是从结构体) 以对象哈希值计算为例,有这样一个函数接口: Py_hash_tPyObject_Hash(PyObject *v){ // ...} 对于浮点数对象和整数对象: PyObject *fo = PyFloatObject_FromDouble(3.14);PyObject_Hash(fo);PyObject *lo = PyLongObject_FromLong(100);PyObject_Hash(lo); 可以看到,对于浮点数对象和整数对象,我们计算对象的哈希值时,调用的都是PyObject_Hash()这个函数,但是对象类型不同,其行为是有区别的,哈希值计算也是如此。 那么在PyObject_Hash函数内部是如何区分的呢? PyObject_Hash()函数具体逻辑: Py_hash_tPyObject_Hash(PyObject *v){ PyTypeObject *tp = Py_TYPE(v); if (tp->Tp_hash! = NULL) return (* tp- > tp_hash) (v); / * To keep to the general practice that inheriting * solely from object in C code should work without * an explicit call to PyType_Ready, we implicitly call * PyType_Ready here and then check the tp_hash slot again * / if (tp- > tp_dict = = NULL) {if (PyType_Ready (tp))
< 0) return -1; if (tp->Tp_hash! = NULL) return (* tp- > tp_hash) (v);} / * Otherwise, the object can't be hashed * / return PyObject_HashNotImplemented (v);}
The function first finds the type of the object through Py_TYPE, and then calls the corresponding hash function through the tp_hash function pointer of the type object.
That is, the PyObject_Hash () function calls different versions of the function according to the type of the object, which is called polymorphism.
4 the behavior of the object
In addition to the tp_hash field, the PyTypeObject structure defines a number of function pointers that eventually point to a function or are empty. We can think of these function pointers as operations defined in type objects that determine the behavior of the corresponding instance objects at run time.
Although the behavior common to the corresponding instance objects is preserved in different types of objects, there are some commonalities among different types of objects. For example, integer objects and floating-point objects support erasing total, such as addition, subtraction, multiplication and division, and tuple objects and list objects both support subscript operations. Therefore, we classify objects according to the criteria of behavior:
Based on this, Python defines a standard set of actions for each category:
The PyNumberMethods structure defines numeric operations
The PySequenceMethods structure defines sequential operations
The PyMappingMethods structure defines associative operations
If the type object provides the relevant set of operations, the corresponding instance object has the corresponding behavior:
Typedef struct _ typeobject {PyObject_VAR_HEAD const char * tp_name; / * For printing, in format "." * / Py_ssize_t tp_basicsize, tp_itemsize; / * For allocation * /. PyNumberMethods * tp_as_number; PySequenceMethods * tp_as_sequence; PyMappingMethods * tp_as_mapping; / /...} PyTypeObject
Taking float as an example, the three fields of the type object PyFloat_Type are initialized as follows:
PyTypeObject PyFloat_Type = {PyVarObject_HEAD_INIT (& PyType_Type, 0) "float", sizeof (PyFloatObject), / /... & float_as_number, / * tp_as_number * / 0, / * tp_as_sequence * / 0 / * tp_as_mapping * /.}
As you can see, only tp_as_number is not empty, that is, float objects support numeric operations, but not sequential and associative operations.
5 reference count
In Python, many scenarios involve adjusting the reference count:
Variable assignment
Function parameter transfer
Attribute operation
Container operation
This is the end of the content of "Life cycle Source Code Analysis of Python objects". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.