python源码剖析_Python源码剖析 - 对象初探
01 前言
對(duì)象是 python 中最核心的一個(gè)概念,在python的世界中,一切都是對(duì)象,整數(shù)、字符串、甚至類型、整數(shù)類型、字符串類型,都是對(duì)象。
02 什么是PyObject
Python 中凡事皆對(duì)象,而其中 PyObject 又是所有對(duì)象的基礎(chǔ),它是 Python 對(duì)象機(jī)制的核心。因?yàn)樗腔?#xff0c;而其他對(duì)象都是對(duì)它的繼承。
打開 Include/python.h 中聲明如下:
#define PyObject_HEAD \
_PyObject_HEAD_EXTRA \
Py_ssize_t ob_refcnt; \
struct _typeobject *ob_type;
typedef struct _object {
PyObject_HEAD
} PyObject;
PyObject 有兩個(gè)重要的成員對(duì)象:
ob_refcnt - 表示引用計(jì)數(shù),當(dāng)有一個(gè)新的 PyObject * 引用該對(duì)象時(shí)候,則進(jìn)行 +1 操作;同時(shí),當(dāng)這個(gè) PyObject * 被刪除時(shí),該引用計(jì)數(shù)就會(huì)減小。當(dāng)計(jì)數(shù)為0時(shí),該對(duì)象就會(huì)被回收,等待內(nèi)存被釋放。
ob_type 記錄對(duì)象的類型信息,這個(gè)結(jié)構(gòu)體含有很多信息,見如下代碼分析。
03 類型對(duì)象
在python中,預(yù)先定義了一些類型對(duì)象,比如 int 類型、str 類型、dict 類型等,這些我們稱之為內(nèi)建類型對(duì)象,這些類型對(duì)象實(shí)現(xiàn)了面向?qū)ο笾?#34;類"的概念。
這些內(nèi)建對(duì)象實(shí)例化之后,可以創(chuàng)建類型對(duì)象所對(duì)應(yīng)的實(shí)例對(duì)象,比如 int 對(duì)象、str 對(duì)象、dict 對(duì)象。這些實(shí)例對(duì)象可以視為面向?qū)ο罄碚撝械摹皩?duì)象"這個(gè)概念在python中的體現(xiàn)。
#define PyObject_VAR_HEAD \
PyObject_HEAD \
Py_ssize_t ob_size; /* Number of items in variable part */
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "." */
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
cmpfunc tp_compare;
reprfunc tp_repr;
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/* Attribute descriptor and subclassing stuff */
struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
descrsetfunc tp_descr_set;
Py_ssize_t tp_dictoffset;
initproc tp_init;
allocfunc tp_alloc;
newfunc tp_new;
freefunc tp_free; /* Low-level free-memory routine */
inquiry tp_is_gc; /* For PyObject_IS_GC */
PyObject *tp_bases;
PyObject *tp_mro; /* method resolution order */
PyObject *tp_cache;
PyObject *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
...
} PyTypeObject;
這當(dāng)中,我們需要關(guān)注幾個(gè)重點(diǎn)成員變量:
tp_name 即類型名稱,例如 'int', tuple', 'list'等,可以標(biāo)準(zhǔn)輸出
tp_basicsize 與 tp_itemsize, 創(chuàng)建該對(duì)象的內(nèi)存信息
關(guān)聯(lián)操作
描述該類型的其他信息
04 定長對(duì)象與變長對(duì)象
定長對(duì)象比較好理解,例如一個(gè)整數(shù)對(duì)象,無論這個(gè)數(shù)值多大,它的存儲(chǔ)長度是一定的,這個(gè)長度由 _typeobject 來指定,不會(huì)變化。
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
變長對(duì)象在內(nèi)存中的長度是不一定的,所以需要 ob_size 來記錄變長部分的個(gè)數(shù),需要注意的是,這個(gè)并不是字節(jié)的數(shù)目。
#define PyObject_VAR_HEAD \
PyObject_HEAD \
Py_ssize_t ob_size; /* Number of items in variable part */
typedef struct {
PyObject_VAR_HEAD;
long ob_shash;
int ob_sstate;
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
* ob_sstate != 0 iff the string object is in stringobject.c's
* 'interned' dictionary; in this case the two references
* from 'interned' to this object are *not counted* in ob_refcnt.
*/
} PyStringObject;
05 創(chuàng)建一個(gè)定長對(duì)象的例子
代碼如下:
a = int(10)
Python 主要做了以下操作:
第一步:分析需要?jiǎng)?chuàng)建的類型,如上,則是 PyInt_Type
第二步:根據(jù) PyInt_Type 中的 int_new 函數(shù)來構(gòu)造對(duì)象
第三步:識(shí)別上述代碼中的 10 為字符傳,然后調(diào)用 PyInt_FromString() 函數(shù)來構(gòu)造
第四步:最后調(diào)用 PyInt_FromLong(long ival) 函數(shù)來進(jìn)行整數(shù)對(duì)象的內(nèi)存分配和賦值。
我們先看一下 PyInt_Type的代碼實(shí)現(xiàn):
tp_name 被賦值為“int”,這樣在 type() 函數(shù)時(shí),就會(huì)顯示該字符串
指定 “int” 類的關(guān)聯(lián)操作,如釋放、打印、比較等
tp_basicsize 賦值為 sizeof(PyIntObject)
tp_itemsize 賦值為 0
PyTypeObject PyInt_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"int",
sizeof(PyIntObject),
0,
(destructor)int_dealloc, /* tp_dealloc */
(printfunc)int_print, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
(cmpfunc)int_compare, /* tp_compare */
(reprfunc)int_to_decimal_string, /* tp_repr */
&int_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)int_hash, /* tp_hash */
0, /* tp_call */
(reprfunc)int_to_decimal_string, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_INT_SUBCLASS, /* tp_flags */
int_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
int_methods, /* tp_methods */
0, /* tp_members */
int_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
int_new, /* tp_new */
};
這里我們對(duì) int_new 方法進(jìn)行展開, int_new 方法就是創(chuàng)建函數(shù),類似于 C++ 中的構(gòu)造函數(shù),用來生成PyIntObject 代碼如下:
static PyObject *
int_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *x = NULL;
int base = -909;
static char *kwlist[] = {"x", "base", 0};
if (type != &PyInt_Type)
return int_subtype_new(type, args, kwds); /* Wimp out */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:int", kwlist,
&x, &base))
return NULL;
if (x == NULL) {
if (base != -909) {
PyErr_SetString(PyExc_TypeError,
"int() missing string argument");
return NULL;
}
return PyInt_FromLong(0L);
}
if (base == -909)
return PyNumber_Int(x);
if (PyString_Check(x)) {
/* Since PyInt_FromString doesn't have a length parameter,
* check here for possible NULs in the string. */
char *string = PyString_AS_STRING(x);
if (strlen(string) != PyString_Size(x)) {
/* create a repr() of the input string,
* just like PyInt_FromString does */
PyObject *srepr;
srepr = PyObject_Repr(x);
if (srepr == NULL)
return NULL;
PyErr_Format(PyExc_ValueError,
"invalid literal for int() with base %d: %s",
base, PyString_AS_STRING(srepr));
Py_DECREF(srepr);
return NULL;
}
return PyInt_FromString(string, NULL, base);
}
#ifdef Py_USING_UNICODE
if (PyUnicode_Check(x))
return PyInt_FromUnicode(PyUnicode_AS_UNICODE(x),
PyUnicode_GET_SIZE(x),
base);
#endif
PyErr_SetString(PyExc_TypeError,
"int() can't convert non-string with explicit base");
return NULL;
}
最后通過 PyInt_FromLong 方法對(duì)新產(chǎn)生的對(duì)象的type信息就行賦值為 PyInt_Type,并設(shè)置整數(shù)的具體數(shù)值。其中如果是小整數(shù),則可以從 small_ints 數(shù)組中直接放回。
#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;
static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;
PyObject *
PyInt_FromLong(long ival)
{
register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
#ifdef COUNT_ALLOCS
if (ival >= 0)
quick_int_allocs++;
else
quick_neg_int_allocs++;
#endif
return (PyObject *) v;
}
#endif
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}
06 展開
為了性能考慮,python 中對(duì)小整數(shù)有專門的緩存池,這樣就不需要每次使用小整數(shù)對(duì)象時(shí)去用 malloc 分配內(nèi)存以及free釋放內(nèi)存。
小整數(shù)之外的大整數(shù)怎么避免重復(fù)分配和回收內(nèi)存呢?
Python 的方案是 PyIntBlock。PyIntBlock 這個(gè)結(jié)構(gòu)就是一塊內(nèi)存,里面保存 PyIntObject 對(duì)象。一個(gè) PyIntBlock 默認(rèn)存放 N_INTOBJECTS 對(duì)象。
PyIntBlock 鏈表通過 block_list 維護(hù),每個(gè)block中都維護(hù)一個(gè) PyIntObject 數(shù)組 objects,block 的 objects 可能會(huì)有些內(nèi)存空閑,因此需要另外用一個(gè) free_list 鏈表串起來這些空閑的項(xiàng)以方便再次使用。objects 數(shù)組中的 PyIntObject 對(duì)象通過 ob_type 字段從后往前鏈接。
小整數(shù)的緩存池最終實(shí)現(xiàn)也是生存在 block_list 維護(hù)的內(nèi)存上,在 python 初始化時(shí),會(huì)調(diào)用 PyInt_Init 函數(shù)申請(qǐng)內(nèi)存并創(chuàng)建小整數(shù)對(duì)象。
更多內(nèi)容
如果你對(duì)Python語言感興趣,可以關(guān)注我,或者關(guān)注我的微信公眾號(hào):xtuz666
總結(jié)
以上是生活随笔為你收集整理的python源码剖析_Python源码剖析 - 对象初探的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php if require,关于php
- 下一篇: python游戏程序编码_python实