CPython对象模型:整型
程序中,最常用的數(shù)據(jù)類型之一就是整型了。 本篇博文記錄的就是研究整型過程中的一些心得。
1 PyLongObject
1.1 版本之別
在python2.x中,整型對象還有兩種:不太大的整數(shù)int(約等于C語言中l(wèi)ong)和大整數(shù)long。 在python3之后,這兩種類型合并為int,但新的int類型的表現(xiàn)和2.x中的long其實(shí)更為接近。
在python2.x中,int是一個(gè)定長的類型,并且采用了兩個(gè)不同的內(nèi)存池分別存放小整數(shù)和大整數(shù); 但在python3之后,int變成了變長對象, 且只有小整數(shù)還放在一個(gè)內(nèi)存池中,大整數(shù)并未采用此技術(shù)。(內(nèi)存池詳解見《python源碼剖析》2.2)
在開始后面的內(nèi)容之前再說明一下: 雖然python3中整型的類型名叫做int,可是它在源碼中的名字全都是PyLongXXX之類的, 找不到PyIntXXX是一件正常的事情。
本文涉及文件:
- Include/longobject.h
- Include/longintrepr.h
- Objects/longobject.c
1.2 PyLongObject
表示整型對象的數(shù)據(jù)結(jié)構(gòu)是PyLongType,定義如下:
/* file:Include/longintrepr.h */struct _longobject {PyObject_VAR_HEAD /* 展開后為PyVarObject ob_base; */digit ob_digit[1];}; /* file:Include/longobject.h */typedef struct _longobject PyLongObject;?
python有兩套數(shù)的表示法(也就是digit這個(gè)類型有兩種定義), 一個(gè)數(shù)用30個(gè)二進(jìn)制位來表示,存儲(chǔ)在unsigned int中, 另一個(gè)用15個(gè)二進(jìn)制位來表示,存儲(chǔ)在unsigned short中, 宏P(guān)YLONG_BITS_IN_DIGIT決定了使用哪個(gè)表示法(該宏定義在Include/pyport.h中或者在configure時(shí)設(shè)定)。 在后文中,你會(huì)發(fā)現(xiàn)關(guān)于這兩套不同表示的一些處理。
在PyLongObject的定義中,采用了一個(gè)digit類型的數(shù)組來存儲(chǔ)數(shù)。 對于那些需要多個(gè)digit來存儲(chǔ)的數(shù),可以通過數(shù)組越界的神奇方法(這里真是蠻拼的) 通過ob_digit來訪問,因?yàn)樵诜峙鋬?nèi)存時(shí)會(huì)根據(jù)數(shù)的大小在ob_digit后分配一些空余空間正好用來越界(真的蠻拼的= =)。
一個(gè)需要多個(gè)digit存儲(chǔ)的數(shù)是以什么樣的順序存儲(chǔ)的呢?請看后文詳解。
1.3 類型對象
基礎(chǔ)篇說過,每一個(gè)內(nèi)置類型都由一個(gè)對應(yīng)的PyTypeObject類型的變量用來保存這個(gè)類型相關(guān)的各種信息。 在整型中,這個(gè)家伙的定義如下:
/* file: Objects/longobject.c */PyTypeObject PyLong_Type = {PyVarObject_HEAD_INIT(&PyType_Type, 0)"int", /* tp_name */offsetof(PyLongObject, ob_digit), /* tp_basicsize */sizeof(digit), /* tp_itemsize */long_dealloc, /* tp_dealloc */0, /* tp_print */0, /* tp_getattr */0, /* tp_setattr */0, /* tp_reserved */long_to_decimal_string, /* tp_repr */&long_as_number, /* tp_as_number */0, /* tp_as_sequence */0, /* tp_as_mapping */(hashfunc)long_hash, /* tp_hash */0, /* tp_call */long_to_decimal_string, /* tp_str */PyObject_GenericGetAttr, /* tp_getattro */0, /* tp_setattro */0, /* tp_as_buffer */Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |Py_TPFLAGS_LONG_SUBCLASS, /* tp_flags */long_doc, /* tp_doc */0, /* tp_traverse */0, /* tp_clear */long_richcompare, /* tp_richcompare */0, /* tp_weaklistoffset */0, /* tp_iter */0, /* tp_iternext */long_methods, /* tp_methods */0, /* tp_members */long_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 */long_new, /* tp_new */PyObject_Del, /* tp_free */};關(guān)于類型對象中的各項(xiàng)的意思在此就略過不提了。
2 整型對象的創(chuàng)建
python中存在多個(gè)用來創(chuàng)建整型對象的函數(shù),但它們之間的差異并不大, 因此這里只分析一個(gè)典型的例子:PyLong_FromLong。
2.1 小整數(shù)對象池
由于整數(shù)(特別是小整數(shù))在程序中會(huì)被用到的次數(shù)太多, 因此如果每次使用整數(shù)都去分配內(nèi)存、使用后再釋放內(nèi)存就會(huì)使得效率極其低。 為了提高效率,python對于小整數(shù)使用了對象池技術(shù)。
python會(huì)事先劃定固定大小的內(nèi)存空間用來存儲(chǔ)小整數(shù)對象, 并且這些小整數(shù)對象一旦創(chuàng)建會(huì)一直持續(xù)到python進(jìn)程結(jié)束才被釋放。
這個(gè)對象池的定義如下:
/* file:Objects/longobject.c */ static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];NSMALLNEGINTS和NSMALLPOSINTS規(guī)定了小整數(shù)的范圍, 任意小整數(shù)x都滿足 -NSMALLNEGINTS ≤ x < NSMALLPOSINTS。
在python解釋器啟動(dòng)時(shí),會(huì)調(diào)用_PyLong_Init()函數(shù)進(jìn)行初始化, 初始化后small_ints數(shù)組內(nèi)的對象都會(huì)被初始化,之后使用只需要直接引用即可。
在創(chuàng)建整型對象時(shí),會(huì)執(zhí)行小整數(shù)檢查:
/* file:Objects/longobject.c */ static PyObject * get_small_int(sdigit ival) {PyObject *v;assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];Py_INCREF(v); /* 該對象引用數(shù)增加 */ #ifdef COUNT_ALLOCSif (ival >= 0)quick_int_allocs++;elsequick_neg_int_allocs++; #endifreturn v; } #define CHECK_SMALL_INT(ival) \do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \return get_small_int((sdigit)ival); \} while(0)CHECK_SMALL_INT宏都在各種創(chuàng)建整數(shù)對象的函數(shù)內(nèi)被使用, 因此不要奇怪出現(xiàn)了return。 若需要?jiǎng)?chuàng)建的數(shù)在小整數(shù)范圍內(nèi), 就會(huì)調(diào)用get_small_int函數(shù)從對象池中尋找并返回對應(yīng)的對象。
2.2 其他整數(shù)
相比于小整數(shù),其他整數(shù)的創(chuàng)建就麻煩的多了。這里以PyLong_FromLong為例:
1 /* file:Objects/longobject.c */ 2 /* Create a new int object from a C long int */ 3 4 PyObject * 5 PyLong_FromLong(long ival) 6 { 7 PyLongObject *v; 8 unsigned long abs_ival; 9 unsigned long t; /* unsigned so >> doesn't propagate sign bit */ 10 int ndigits = 0; 11 int sign = 1; 12 13 CHECK_SMALL_INT(ival); 14 15 if (ival < 0) { 16 /* negate: can't write this as abs_ival = -ival since that 17 invokes undefined behaviour when ival is LONG_MIN */ 18 abs_ival = 0U-(unsigned long)ival; 19 sign = -1; 20 } 21 else { 22 abs_ival = (unsigned long)ival; 23 } 24 25 /* Fast path for single-digit ints */ 26 if (!(abs_ival >> PyLong_SHIFT)) { 27 v = _PyLong_New(1); 28 if (v) { 29 Py_SIZE(v) = sign; 30 v->ob_digit[0] = Py_SAFE_DOWNCAST( 31 abs_ival, unsigned long, digit); 32 } 33 return (PyObject*)v; 34 } 35 36 #if PyLong_SHIFT==15 37 /* 2 digits */ 38 if (!(abs_ival >> 2*PyLong_SHIFT)) { 39 v = _PyLong_New(2); 40 if (v) { 41 Py_SIZE(v) = 2*sign; 42 v->ob_digit[0] = Py_SAFE_DOWNCAST( 43 abs_ival & PyLong_MASK, unsigned long, digit); 44 v->ob_digit[1] = Py_SAFE_DOWNCAST( 45 abs_ival >> PyLong_SHIFT, unsigned long, digit); 46 } 47 return (PyObject*)v; 48 } 49 #endif 50 51 /* Larger numbers: loop to determine number of digits */ 52 t = abs_ival; 53 while (t) { 54 ++ndigits; 55 t >>= PyLong_SHIFT; 56 } 57 v = _PyLong_New(ndigits); 58 if (v != NULL) { 59 digit *p = v->ob_digit; 60 Py_SIZE(v) = ndigits*sign; 61 t = abs_ival; 62 while (t) { 63 *p++ = Py_SAFE_DOWNCAST( 64 t & PyLong_MASK, unsigned long, digit); 65 t >>= PyLong_SHIFT; 66 } 67 } 68 return (PyObject *)v; 69 } 70 71 PyLongObject * 72 _PyLong_New(Py_ssize_t size) 73 { 74 PyLongObject *result; 75 /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + 76 sizeof(digit)*size. Previous incarnations of this code used 77 sizeof(PyVarObject) instead of the offsetof, but this risks being 78 incorrect in the presence of padding between the PyVarObject header 79 and the digits. */ 80 if (size > (Py_ssize_t)MAX_LONG_DIGITS) { 81 PyErr_SetString(PyExc_OverflowError, 82 "too many digits in integer"); 83 return NULL; 84 } 85 result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) + 86 size*sizeof(digit)); 87 if (!result) { 88 PyErr_NoMemory(); 89 return NULL; 90 } 91 return (PyLongObject*)PyObject_INIT_VAR(result, &PyLong_Type, size); 92 }調(diào)用該函數(shù)后,先用CHECK_SMALL_INT來檢查小整數(shù),這個(gè)在前面已經(jīng)分析過了。
若不是小整數(shù),首先需要求出ival的絕對值(15~23行)。 對于負(fù)數(shù),需要注意它求絕對值的方法:用0減而不是直接取反。 這是由于最小的long取反后會(huì)導(dǎo)致溢出。以64位long為例, 最小的long是-2^63,最大是2^63 - 1,取反后溢出可能發(fā)生神奇的事情。
在代碼的第25~33行,是針對一個(gè)digit能存儲(chǔ)的情況做的優(yōu)化。 調(diào)用_PyLong_New分配內(nèi)存后,設(shè)置v的大小和ob_digit值并返回即可。 用來設(shè)置值的Py_SAFE_DOWNCAST是一個(gè)宏,它的定義在非DEBUG模式下如下:
#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE) /* 其實(shí)就是直接進(jìn)行類型轉(zhuǎn)換進(jìn)行截?cái)唷? 在DEBUG模式下會(huì)有更復(fù)雜的表現(xiàn)*/在36~49行,是針對15位digit、兩個(gè)digit存儲(chǔ)的情況做的優(yōu)化。 在設(shè)置值時(shí),
ob_digit[0]中存放的是后15的值(PyLong_MASK的值是0x7FFF,與abs_ival做按位與得到后15位), ob_digit[1]中存放的自然是前15位了。這里就可以解答上面提出的一個(gè)問題了,多個(gè)digit保存的情況下,到底是以怎樣的順序存放的呢? 答案就是,對于b位的digit,
ob_digit[n]中存放的是從低位起從零計(jì)數(shù)的第(n-1)*b到第n*b-1位。在51~68行,是對于其他情況的一個(gè)普遍處理方法,這里就不詳細(xì)分析了。
需要注意的是,size會(huì)決定一個(gè)數(shù)的正負(fù)(因?yàn)閛b_digit存儲(chǔ)的都是絕對值后的結(jié)果)負(fù)數(shù)的話是digit數(shù)的相反數(shù),0的話是0,正數(shù)是digit數(shù)。
最后,再分析一下_PyLong_new這個(gè)函數(shù)。 這個(gè)函數(shù)中最關(guān)鍵的是計(jì)算該對象的大小。這里采用了如下計(jì)算方式:
對象大小 = ob_digit偏移量 + size*sizeof(digit)再看看offsetof這個(gè)宏:
# define offsetof(type, member) ((size_t)(&((type *)0)->member))以0為基地址取member的地址再轉(zhuǎn)換成size_t類型就是其偏移。
3 其他操作分析
最后,再看一個(gè)典型的操作來看看python是如何處理整型數(shù)據(jù)的。 這些操作在PyLong_Type的tp_as_number成員中可以看到。
3.1 加法
加法定義如下:
/* file:Objects/longobject.c */long_add(PyLongObject *a, PyLongObject *b) {PyLongObject *z;CHECK_BINOP(a, b);if (ABS(Py_SIZE(a)) <= 1 && ABS(Py_SIZE(b)) <= 1) {PyObject *result = PyLong_FromLong(MEDIUM_VALUE(a) +MEDIUM_VALUE(b));return result;}if (Py_SIZE(a) < 0) {if (Py_SIZE(b) < 0) {z = x_add(a, b);if (z != NULL && Py_SIZE(z) != 0)Py_SIZE(z) = -(Py_SIZE(z));}elsez = x_sub(b, a);}else {if (Py_SIZE(b) < 0)z = x_sub(a, b);elsez = x_add(a, b);}return (PyObject *)z; }?
python中的整型是不可變對象,因此每一次運(yùn)算都會(huì)產(chǎn)生新的整型對象。 看到這里我們會(huì)自然的產(chǎn)生疑問,如果在python里重復(fù)計(jì)算兩個(gè)非小整數(shù)相加, 會(huì)不會(huì)一直產(chǎn)生新的對象呢?還是會(huì)引用到原先的對象呢? 從目前分析過的代碼中,我們可以猜測是會(huì)產(chǎn)生新的對象;后文會(huì)給出驗(yàn)證方法。
在上面的函數(shù)中,主要工作就是對size進(jìn)行判斷并調(diào)用對應(yīng)的函數(shù); 因此更核心的功能在x_add和x_sub中。這里只分析x_add:
1 /* file:Objects/longobject.c */ 2 static PyLongObject * 3 x_add(PyLongObject *a, PyLongObject *b) 4 { 5 Py_ssize_t size_a = ABS(Py_SIZE(a)), size_b = ABS(Py_SIZE(b)); 6 PyLongObject *z; 7 Py_ssize_t i; 8 digit carry = 0; 9 10 /* Ensure a is the larger of the two: */ 11 if (size_a < size_b) { 12 { PyLongObject *temp = a; a = b; b = temp; } 13 { Py_ssize_t size_temp = size_a; 14 size_a = size_b; 15 size_b = size_temp; } 16 } 17 z = _PyLong_New(size_a+1); 18 if (z == NULL) 19 return NULL; 20 for (i = 0; i < size_b; ++i) { 21 carry += a->ob_digit[i] + b->ob_digit[i]; 22 z->ob_digit[i] = carry & PyLong_MASK; 23 carry >>= PyLong_SHIFT; 24 } 25 for (; i < size_a; ++i) { 26 carry += a->ob_digit[i]; 27 z->ob_digit[i] = carry & PyLong_MASK; 28 carry >>= PyLong_SHIFT; 29 } 30 z->ob_digit[i] = carry; 31 return long_normalize(z); 32 }首先,確保a是size較大的一個(gè)(10~15行)。
之后,為新的對象分配size_a+1的空間。
然后,對于ob_digit中每一個(gè)元素分別相加,所得結(jié)果與PyLong_MASK按位與再存入新對象對應(yīng)ob_digit中, 之后carry右移。這樣可以保證超出的部分不會(huì)丟失。 進(jìn)行完所有加法后,把carry放在新對象的對高ob_digit中以防丟失運(yùn)算中溢出的結(jié)果。 最后調(diào)用long_normalize消除前導(dǎo)零(防止size暴漲狂吃內(nèi)存)。
4 Hack it
這里我們主要打print的主意。 在python2.x中,print語句調(diào)用的是類型中的tp_print成員對應(yīng)的函數(shù); 可是python3之后刪除了print語句,print成了內(nèi)建函數(shù),這樣就變的稍微麻煩了一點(diǎn)。
查官網(wǎng)手冊可知,print函數(shù)會(huì)調(diào)用str函數(shù),str函數(shù)對應(yīng)類型中的tp_str。 因此,我們需要更改一下tp_str的內(nèi)容。
改動(dòng)之前,發(fā)現(xiàn)tp_repr和tp_str的內(nèi)容一樣。 由于tp_repr在python編譯中起到了很重要的作用,因此最好把它和tp_str分開。
把tp_str對應(yīng)的函數(shù)long_to_decimal_string復(fù)制粘貼并改個(gè)名字(比如my_long_to_decimal_string), 再把該函數(shù)內(nèi)調(diào)用的long_to_decimal_string_internal復(fù)制粘貼改名字,再把PyLong_Type對應(yīng)的tp_str改了, 就可以放心的下手啦。
PyTypeObject PyLong_Type = {/* ... */long_to_decimal_string, /* tp_repr *//* ... */my_long_to_decimal_string, /* tp_str *//* ... */ }之后就是修改代碼了。在long_to_decimal_string_internal中,進(jìn)行輸出的部分是其中的WRITE_DIGITS宏, 這個(gè)宏會(huì)寫入內(nèi)容到之后打印的字符串內(nèi),而我們需要做的就是修改這個(gè)宏。除此之外,還需要修改以下和字符串長度相關(guān)的部分。
1 /* file:Objects/longobject.c */ 2 long_to_decimal_string_internal(PyObject *aa, 3 PyObject **p_output, 4 _PyUnicodeWriter *writer) 5 { 6 PyLongObject *scratch, *a; 7 PyObject *str; 8 Py_ssize_t size, strlen, size_a, i, j; 9 digit *pout, *pin, rem, tenpow; 10 int negative; 11 enum PyUnicode_Kind kind; 12 13 a = (PyLongObject *)aa; 14 if (a == NULL || !PyLong_Check(a)) { 15 PyErr_BadInternalCall(); 16 return -1; 17 } 18 size_a = ABS(Py_SIZE(a)); 19 negative = Py_SIZE(a) < 0; 20 21 /* quick and dirty upper bound for the number of digits 22 required to express a in base _PyLong_DECIMAL_BASE: 23 24 #digits = 1 + floor(log2(a) / log2(_PyLong_DECIMAL_BASE)) 25 26 But log2(a) < size_a * PyLong_SHIFT, and 27 log2(_PyLong_DECIMAL_BASE) = log2(10) * _PyLong_DECIMAL_SHIFT 28 > 3 * _PyLong_DECIMAL_SHIFT 29 */ 30 if (size_a > PY_SSIZE_T_MAX / PyLong_SHIFT) { 31 PyErr_SetString(PyExc_OverflowError, 32 "int too large to format"); 33 return -1; 34 } 35 /* the expression size_a * PyLong_SHIFT is now safe from overflow */ 36 size = 1 + size_a * PyLong_SHIFT / (3 * _PyLong_DECIMAL_SHIFT); 37 scratch = _PyLong_New(size); 38 if (scratch == NULL) 39 return -1; 40 41 /* convert array of base _PyLong_BASE digits in pin to an array of 42 base _PyLong_DECIMAL_BASE digits in pout, following Knuth (TAOCP, 43 Volume 2 (3rd edn), section 4.4, Method 1b). */ 44 pin = a->ob_digit; 45 pout = scratch->ob_digit; 46 size = 0; 47 for (i = size_a; --i >= 0; ) { 48 digit hi = pin[i]; 49 for (j = 0; j < size; j++) { 50 twodigits z = (twodigits)pout[j] << PyLong_SHIFT | hi; 51 hi = (digit)(z / _PyLong_DECIMAL_BASE); 52 pout[j] = (digit)(z - (twodigits)hi * 53 _PyLong_DECIMAL_BASE); 54 } 55 while (hi) { 56 pout[size++] = hi % _PyLong_DECIMAL_BASE; 57 hi /= _PyLong_DECIMAL_BASE; 58 } 59 /* check for keyboard interrupt */ 60 SIGCHECK({ 61 Py_DECREF(scratch); 62 return -1; 63 }); 64 } 65 /* pout should have at least one digit, so that the case when a = 0 66 works correctly */ 67 if (size == 0) 68 pout[size++] = 0; 69 70 /* calculate exact length of output string, and allocate */ 71 strlen = negative + 1 + (size - 1) * _PyLong_DECIMAL_SHIFT; 72 tenpow = 10; 73 rem = pout[size-1]; 74 while (rem >= tenpow) { 75 tenpow *= 10; 76 strlen++; 77 } 78 if (writer) { 79 if (_PyUnicodeWriter_Prepare(writer, strlen, '9') == -1) { 80 Py_DECREF(scratch); 81 return -1; 82 } 83 kind = writer->kind; 84 str = NULL; 85 } 86 else { 87 str = PyUnicode_New(strlen, '9'); 88 if (str == NULL) { 89 Py_DECREF(scratch); 90 return -1; 91 } 92 kind = PyUnicode_KIND(str); 93 } 94 95 #define WRITE_DIGITS(TYPE) \ 96 do { \ 97 if (writer) \ 98 p = (TYPE*)PyUnicode_DATA(writer->buffer) + writer->pos + strlen; \ 99 else \ 100 p = (TYPE*)PyUnicode_DATA(str) + strlen; \ 101 \ 102 /* pout[0] through pout[size-2] contribute exactly \ 103 _PyLong_DECIMAL_SHIFT digits each */ \ 104 for (i=0; i < size - 1; i++) { \ 105 rem = pout[i]; \ 106 for (j = 0; j < _PyLong_DECIMAL_SHIFT; j++) { \ 107 *--p = '0' + rem % 10; \ 108 rem /= 10; \ 109 } \ 110 } \ 111 /* pout[size-1]: always produce at least one decimal digit */ \ 112 rem = pout[i]; \ 113 do { \ 114 *--p = '0' + rem % 10; \ 115 rem /= 10; \ 116 } while (rem != 0); \ 117 \ 118 /* and sign */ \ 119 if (negative) \ 120 *--p = '-'; \ 121 \ 122 /* check we've counted correctly */ \ 123 if (writer) \ 124 assert(p == ((TYPE*)PyUnicode_DATA(writer->buffer) + writer->pos)); \ 125 else \ 126 assert(p == (TYPE*)PyUnicode_DATA(str)); \ 127 } while (0) 128 129 /* fill the string right-to-left */ 130 if (kind == PyUnicode_1BYTE_KIND) { 131 Py_UCS1 *p; 132 WRITE_DIGITS(Py_UCS1); 133 } 134 else if (kind == PyUnicode_2BYTE_KIND) { 135 Py_UCS2 *p; 136 WRITE_DIGITS(Py_UCS2); 137 } 138 else { 139 Py_UCS4 *p; 140 assert (kind == PyUnicode_4BYTE_KIND); 141 WRITE_DIGITS(Py_UCS4); 142 } 143 #undef WRITE_DIGITS 144 145 Py_DECREF(scratch); 146 if (writer) { 147 writer->pos += strlen; 148 } 149 else { 150 assert(_PyUnicode_CheckConsistency(str, 1)); 151 *p_output = (PyObject *)str; 152 } 153 return 0; 154 }?
上面的代碼中,strlen這個(gè)變量表示了字符串的長度。
為了驗(yàn)證打印的對象是否是同一個(gè),我們用一個(gè)靜態(tài)變量保存上一次要打印的結(jié)果。 若是同一個(gè)對象,則打印出YES并換行;若不是同一個(gè)對象也打印NO并換行。 這樣的改法最多會(huì)增加4個(gè)長度,因此給strlen+4。
在WRITE_DIGITS宏內(nèi)會(huì)倒序排列要輸出的內(nèi)容,因此我們也得倒著加要打印的東西。 在121行后面加入下列代碼:
*--p = '\n'; \ if(pre == aa)\ {\*--p = 'S'; \*--p = 'E'; \*--p = 'Y'; \ }\ else\ {\*--p = ' '; \*--p = 'O'; \*--p = 'N'; \ }\編譯python并運(yùn)行,測試一下print函數(shù):
>>> print(1) NO 1 >>> print(1) YES 1 >>> print(400 + 300) NO 700 >>> print(400 + 300) NO 700 >>> print(700) NO 700 >>> print(700) YES 700 >>> a = 600 + 900 >>> b = 600 + 900 >>> print(a) NO 1500>>> print(a) YES 1500
>>> print(b)
NO
1500
>>> print(600 + 900)
NO
1500
運(yùn)行結(jié)果確實(shí)如猜想那樣,每次加都會(huì)產(chǎn)生值一樣的全新對象。
最后放上更改后的long_to_decimal_string系列函數(shù):
1 static int 2 my_long_to_decimal_string_internal(PyObject *aa, 3 PyObject **p_output, 4 _PyUnicodeWriter *writer) 5 { 6 PyLongObject *scratch, *a; 7 PyObject *str; 8 Py_ssize_t size, strlen, size_a, i, j, tmp; 9 digit *pout, *pin, rem, tenpow; 10 int negative; 11 enum PyUnicode_Kind kind; 12 static PyObject *pre = NULL; 13 14 a = (PyLongObject *)aa; 15 if (a == NULL || !PyLong_Check(a)) { 16 PyErr_BadInternalCall(); 17 return -1; 18 } 19 size_a = ABS(Py_SIZE(a)); 20 negative = Py_SIZE(a) < 0; 21 22 /* quick and dirty upper bound for the number of digits 23 required to express a in base _PyLong_DECIMAL_BASE: 24 25 #digits = 1 + floor(log2(a) / log2(_PyLong_DECIMAL_BASE)) 26 27 But log2(a) < size_a * PyLong_SHIFT, and 28 log2(_PyLong_DECIMAL_BASE) = log2(10) * _PyLong_DECIMAL_SHIFT 29 > 3 * _PyLong_DECIMAL_SHIFT 30 */ 31 if (size_a > PY_SSIZE_T_MAX / PyLong_SHIFT) { 32 PyErr_SetString(PyExc_OverflowError, 33 "int too large to format"); 34 return -1; 35 } 36 /* the expression size_a * PyLong_SHIFT is now safe from overflow */ 37 size = 1 + size_a * PyLong_SHIFT / (3 * _PyLong_DECIMAL_SHIFT); 38 scratch = _PyLong_New(size); 39 if (scratch == NULL) 40 return -1; 41 42 /* convert array of base _PyLong_BASE digits in pin to an array of 43 base _PyLong_DECIMAL_BASE digits in pout, following Knuth (TAOCP, 44 Volume 2 (3rd edn), section 4.4, Method 1b). */ 45 pin = a->ob_digit; 46 pout = scratch->ob_digit; 47 size = 0; 48 for (i = size_a; --i >= 0; ) { 49 digit hi = pin[i]; 50 for (j = 0; j < size; j++) { 51 twodigits z = (twodigits)pout[j] << PyLong_SHIFT | hi; 52 hi = (digit)(z / _PyLong_DECIMAL_BASE); 53 pout[j] = (digit)(z - (twodigits)hi * 54 _PyLong_DECIMAL_BASE); 55 } 56 while (hi) { 57 pout[size++] = hi % _PyLong_DECIMAL_BASE; 58 hi /= _PyLong_DECIMAL_BASE; 59 } 60 /* check for keyboard interrupt */ 61 SIGCHECK({ 62 Py_DECREF(scratch); 63 return -1; 64 }); 65 } 66 /* pout should have at least one digit, so that the case when a = 0 67 works correctly */ 68 if (size == 0) 69 pout[size++] = 0; 70 71 /* calculate exact length of output string, and allocate */ 72 strlen = negative + 1 + (size - 1) * _PyLong_DECIMAL_SHIFT + 4; 73 tenpow = 10; 74 rem = pout[size-1]; 75 while (rem >= tenpow) { 76 tenpow *= 10; 77 strlen++; 78 } 79 if (writer) { 80 if (_PyUnicodeWriter_Prepare(writer, strlen, '9') == -1) { 81 Py_DECREF(scratch); 82 return -1; 83 } 84 kind = writer->kind; 85 str = NULL; 86 } 87 else { 88 str = PyUnicode_New(strlen, '9'); 89 if (str == NULL) { 90 Py_DECREF(scratch); 91 return -1; 92 } 93 kind = PyUnicode_KIND(str); 94 } 95 tmp = Py_SIZE(a); 96 97 #define WRITE_DIGITS(TYPE) \ 98 do { \ 99 if (writer) \ 100 p = (TYPE*)PyUnicode_DATA(writer->buffer) + writer->pos + strlen; \ 101 else \ 102 p = (TYPE*)PyUnicode_DATA(str) + strlen; \ 103 \ 104 /* pout[0] through pout[size-2] contribute exactly \ 105 _PyLong_DECIMAL_SHIFT digits each */ \ 106 for (i=0; i < size - 1; i++) { \ 107 rem = pout[i]; \ 108 for (j = 0; j < _PyLong_DECIMAL_SHIFT; j++) { \ 109 *--p = '0' + rem % 10; \ 110 rem /= 10; \ 111 } \ 112 } \ 113 /* pout[size-1]: always produce at least one decimal digit */ \ 114 rem = pout[i]; \ 115 do { \ 116 *--p = '0' + rem % 10; \ 117 rem /= 10; \ 118 } while (rem != 0); \ 119 \ 120 /* and sign */ \ 121 if (negative) \ 122 *--p = '-'; \ 123 *--p = '\n'; \ 124 if(pre == aa)\ 125 {\ 126 *--p = 'S'; \ 127 *--p = 'E'; \ 128 *--p = 'Y'; \ 129 }\ 130 else\ 131 {\ 132 *--p = ' '; \ 133 *--p = 'O'; \ 134 *--p = 'N'; \ 135 }\ 136 \ 137 /* check we've counted correctly */ \ 138 if (writer) \ 139 assert(p == ((TYPE*)PyUnicode_DATA(writer->buffer) + writer->pos)); \ 140 else \ 141 assert(p == (TYPE*)PyUnicode_DATA(str)); \ 142 } while (0) 143 144 /* fill the string right-to-left */ 145 if (kind == PyUnicode_1BYTE_KIND) { 146 Py_UCS1 *p; 147 WRITE_DIGITS(Py_UCS1); 148 } 149 else if (kind == PyUnicode_2BYTE_KIND) { 150 Py_UCS2 *p; 151 WRITE_DIGITS(Py_UCS2); 152 } 153 else { 154 Py_UCS4 *p; 155 assert (kind == PyUnicode_4BYTE_KIND); 156 WRITE_DIGITS(Py_UCS4); 157 } 158 #undef WRITE_DIGITS 159 160 Py_DECREF(scratch); 161 if (writer) { 162 writer->pos += strlen; 163 } 164 else { 165 assert(_PyUnicode_CheckConsistency(str, 1)); 166 *p_output = (PyObject *)str; 167 } 168 pre = aa; 169 return 0; 170 } 171 172 static PyObject * 173 my_long_to_decimal_string(PyObject *aa) 174 { 175 PyObject *v; 176 if (my_long_to_decimal_string_internal(aa, &v, NULL) == -1) 177 return NULL; 178 return v; 179 }?
轉(zhuǎn)載于:https://www.cnblogs.com/w0mTea/p/4287264.html
總結(jié)
以上是生活随笔為你收集整理的CPython对象模型:整型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 将List中的数据导入csv文件中
- 下一篇: python-I/O-文件操作