CPython对象模型:整型
程序中,最常用的數據類型之一就是整型了。 本篇博文記錄的就是研究整型過程中的一些心得。
1 PyLongObject
1.1 版本之別
在python2.x中,整型對象還有兩種:不太大的整數int(約等于C語言中long)和大整數long。 在python3之后,這兩種類型合并為int,但新的int類型的表現和2.x中的long其實更為接近。
在python2.x中,int是一個定長的類型,并且采用了兩個不同的內存池分別存放小整數和大整數; 但在python3之后,int變成了變長對象, 且只有小整數還放在一個內存池中,大整數并未采用此技術。(內存池詳解見《python源碼剖析》2.2)
在開始后面的內容之前再說明一下: 雖然python3中整型的類型名叫做int,可是它在源碼中的名字全都是PyLongXXX之類的, 找不到PyIntXXX是一件正常的事情。
本文涉及文件:
- Include/longobject.h
- Include/longintrepr.h
- Objects/longobject.c
1.2 PyLongObject
表示整型對象的數據結構是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有兩套數的表示法(也就是digit這個類型有兩種定義), 一個數用30個二進制位來表示,存儲在unsigned int中, 另一個用15個二進制位來表示,存儲在unsigned short中, 宏PYLONG_BITS_IN_DIGIT決定了使用哪個表示法(該宏定義在Include/pyport.h中或者在configure時設定)。 在后文中,你會發現關于這兩套不同表示的一些處理。
在PyLongObject的定義中,采用了一個digit類型的數組來存儲數。 對于那些需要多個digit來存儲的數,可以通過數組越界的神奇方法(這里真是蠻拼的) 通過ob_digit來訪問,因為在分配內存時會根據數的大小在ob_digit后分配一些空余空間正好用來越界(真的蠻拼的= =)。
一個需要多個digit存儲的數是以什么樣的順序存儲的呢?請看后文詳解。
1.3 類型對象
基礎篇說過,每一個內置類型都由一個對應的PyTypeObject類型的變量用來保存這個類型相關的各種信息。 在整型中,這個家伙的定義如下:
/* 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 */};關于類型對象中的各項的意思在此就略過不提了。
2 整型對象的創建
python中存在多個用來創建整型對象的函數,但它們之間的差異并不大, 因此這里只分析一個典型的例子:PyLong_FromLong。
2.1 小整數對象池
由于整數(特別是小整數)在程序中會被用到的次數太多, 因此如果每次使用整數都去分配內存、使用后再釋放內存就會使得效率極其低。 為了提高效率,python對于小整數使用了對象池技術。
python會事先劃定固定大小的內存空間用來存儲小整數對象, 并且這些小整數對象一旦創建會一直持續到python進程結束才被釋放。
這個對象池的定義如下:
/* file:Objects/longobject.c */ static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];NSMALLNEGINTS和NSMALLPOSINTS規定了小整數的范圍, 任意小整數x都滿足 -NSMALLNEGINTS ≤ x < NSMALLPOSINTS。
在python解釋器啟動時,會調用_PyLong_Init()函數進行初始化, 初始化后small_ints數組內的對象都會被初始化,之后使用只需要直接引用即可。
在創建整型對象時,會執行小整數檢查:
/* 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); /* 該對象引用數增加 */ #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宏都在各種創建整數對象的函數內被使用, 因此不要奇怪出現了return。 若需要創建的數在小整數范圍內, 就會調用get_small_int函數從對象池中尋找并返回對應的對象。
2.2 其他整數
相比于小整數,其他整數的創建就麻煩的多了。這里以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 }調用該函數后,先用CHECK_SMALL_INT來檢查小整數,這個在前面已經分析過了。
若不是小整數,首先需要求出ival的絕對值(15~23行)。 對于負數,需要注意它求絕對值的方法:用0減而不是直接取反。 這是由于最小的long取反后會導致溢出。以64位long為例, 最小的long是-2^63,最大是2^63 - 1,取反后溢出可能發生神奇的事情。
在代碼的第25~33行,是針對一個digit能存儲的情況做的優化。 調用_PyLong_New分配內存后,設置v的大小和ob_digit值并返回即可。 用來設置值的Py_SAFE_DOWNCAST是一個宏,它的定義在非DEBUG模式下如下:
#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE) /* 其實就是直接進行類型轉換進行截斷。* 在DEBUG模式下會有更復雜的表現*/在36~49行,是針對15位digit、兩個digit存儲的情況做的優化。 在設置值時,
ob_digit[0]中存放的是后15的值(PyLong_MASK的值是0x7FFF,與abs_ival做按位與得到后15位), ob_digit[1]中存放的自然是前15位了。這里就可以解答上面提出的一個問題了,多個digit保存的情況下,到底是以怎樣的順序存放的呢? 答案就是,對于b位的digit,
ob_digit[n]中存放的是從低位起從零計數的第(n-1)*b到第n*b-1位。在51~68行,是對于其他情況的一個普遍處理方法,這里就不詳細分析了。
需要注意的是,size會決定一個數的正負(因為ob_digit存儲的都是絕對值后的結果)負數的話是digit數的相反數,0的話是0,正數是digit數。
最后,再分析一下_PyLong_new這個函數。 這個函數中最關鍵的是計算該對象的大小。這里采用了如下計算方式:
對象大小 = ob_digit偏移量 + size*sizeof(digit)再看看offsetof這個宏:
# define offsetof(type, member) ((size_t)(&((type *)0)->member))以0為基地址取member的地址再轉換成size_t類型就是其偏移。
3 其他操作分析
最后,再看一個典型的操作來看看python是如何處理整型數據的。 這些操作在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中的整型是不可變對象,因此每一次運算都會產生新的整型對象。 看到這里我們會自然的產生疑問,如果在python里重復計算兩個非小整數相加, 會不會一直產生新的對象呢?還是會引用到原先的對象呢? 從目前分析過的代碼中,我們可以猜測是會產生新的對象;后文會給出驗證方法。
在上面的函數中,主要工作就是對size進行判斷并調用對應的函數; 因此更核心的功能在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較大的一個(10~15行)。
之后,為新的對象分配size_a+1的空間。
然后,對于ob_digit中每一個元素分別相加,所得結果與PyLong_MASK按位與再存入新對象對應ob_digit中, 之后carry右移。這樣可以保證超出的部分不會丟失。 進行完所有加法后,把carry放在新對象的對高ob_digit中以防丟失運算中溢出的結果。 最后調用long_normalize消除前導零(防止size暴漲狂吃內存)。
4 Hack it
這里我們主要打print的主意。 在python2.x中,print語句調用的是類型中的tp_print成員對應的函數; 可是python3之后刪除了print語句,print成了內建函數,這樣就變的稍微麻煩了一點。
查官網手冊可知,print函數會調用str函數,str函數對應類型中的tp_str。 因此,我們需要更改一下tp_str的內容。
改動之前,發現tp_repr和tp_str的內容一樣。 由于tp_repr在python編譯中起到了很重要的作用,因此最好把它和tp_str分開。
把tp_str對應的函數long_to_decimal_string復制粘貼并改個名字(比如my_long_to_decimal_string), 再把該函數內調用的long_to_decimal_string_internal復制粘貼改名字,再把PyLong_Type對應的tp_str改了, 就可以放心的下手啦。
PyTypeObject PyLong_Type = {/* ... */long_to_decimal_string, /* tp_repr *//* ... */my_long_to_decimal_string, /* tp_str *//* ... */ }之后就是修改代碼了。在long_to_decimal_string_internal中,進行輸出的部分是其中的WRITE_DIGITS宏, 這個宏會寫入內容到之后打印的字符串內,而我們需要做的就是修改這個宏。除此之外,還需要修改以下和字符串長度相關的部分。
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這個變量表示了字符串的長度。
為了驗證打印的對象是否是同一個,我們用一個靜態變量保存上一次要打印的結果。 若是同一個對象,則打印出YES并換行;若不是同一個對象也打印NO并換行。 這樣的改法最多會增加4個長度,因此給strlen+4。
在WRITE_DIGITS宏內會倒序排列要輸出的內容,因此我們也得倒著加要打印的東西。 在121行后面加入下列代碼:
*--p = '\n'; \ if(pre == aa)\ {\*--p = 'S'; \*--p = 'E'; \*--p = 'Y'; \ }\ else\ {\*--p = ' '; \*--p = 'O'; \*--p = 'N'; \ }\編譯python并運行,測試一下print函數:
>>> 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
運行結果確實如猜想那樣,每次加都會產生值一樣的全新對象。
最后放上更改后的long_to_decimal_string系列函數:
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 }?
轉載于:https://www.cnblogs.com/w0mTea/p/4287264.html
總結
以上是生活随笔為你收集整理的CPython对象模型:整型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 将List中的数据导入csv文件中
- 下一篇: python-I/O-文件操作