Python中的元組容器序列(tuple)與列表容器序列(list)具有極大的相似之處,因此也常被稱(chēng)為不可變的列表。
但是兩者之間也有很多的差距,元組側(cè)重于數(shù)據(jù)的展示,而列表側(cè)重于數(shù)據(jù)的存儲(chǔ)與操作。
它們非常相似,雖然都可以存儲(chǔ)任意類(lèi)型的數(shù)據(jù),但是一個(gè)元組定義好之后就不能夠再進(jìn)行修改。
元組的特點(diǎn):
基本聲明
以下是使用類(lèi)實(shí)例化的形式進(jìn)行對(duì)象聲明:
tup = tuple((1, 2, 3, 4, 5)) print("值:%r,類(lèi)型:%r" % (tup, type(tup))) # 值:(1, 2, 3, 4, 5),類(lèi)型:class 'tuple'>
也可以選擇使用更方便的字面量形式進(jìn)行對(duì)象聲明,使用逗號(hào)對(duì)數(shù)據(jù)項(xiàng)之間進(jìn)行分割:
tup = 1, 2, 3, 4, 5 print("值:%r,類(lèi)型:%r" % (tup, type(tup))) # 值:(1, 2, 3, 4, 5),類(lèi)型:class 'tuple'>
為了美觀,我們一般會(huì)在兩側(cè)加上(),但是要確定一點(diǎn),元組定義是逗號(hào)分隔的數(shù)據(jù)項(xiàng),而并非是()包裹的數(shù)據(jù)項(xiàng):
tup = (1, 2, 3, 4, 5) print("值:%r,類(lèi)型:%r" % (tup, type(tup))) # 值:(1, 2, 3, 4, 5),類(lèi)型:class 'tuple'>
當(dāng)一個(gè)元組中嵌套另一個(gè)元組,該元組就可以稱(chēng)為多維元組。
如下,定義一個(gè)2維元組:
tup = (1, 2, 3, 4, 5) print("值:%r,類(lèi)型:%r" % (tup, type(tup))) # 值:(1, 2, 3, 4, 5),類(lèi)型:class 'tuple'>
在Python中,元組中的數(shù)據(jù)項(xiàng)如果過(guò)多,可能會(huì)導(dǎo)致整個(gè)元組太長(zhǎng),太長(zhǎng)的元組是不符合PEP8規(guī)范的。
每行最大的字符數(shù)不可超過(guò)79,文檔字符或者注釋每行不可超過(guò)72
Python雖然提供了續(xù)行符\,但是在元組中可以忽略續(xù)行符,如下所示:
tup = (1, 2, ("三", "四")) print("值:%r,類(lèi)型:%r" % (tup, type(tup))) # 值:(1, 2, ('三', '四')),類(lèi)型:class 'tuple'>
元組支持與布爾型、字符串、列表、以及集合類(lèi)型進(jìn)行類(lèi)型轉(zhuǎn)換:
tup = (1, 2, 3) bTup = bool(tup) # 布爾類(lèi)型 strTup = str(tup) # 字符串類(lèi)型 liTup = list(tup) # 列表類(lèi)型 setTup = set(tup) # 集合類(lèi)型 print("值:%r,類(lèi)型:%r" % (bTup, type(bTup))) print("值:%r,類(lèi)型:%r" % (strTup, type(strTup))) print("值:%r,類(lèi)型:%r" % (liTup, type(liTup))) print("值:%r,類(lèi)型:%r" % (setTup, type(setTup))) # 值:True,類(lèi)型:class 'bool'> # 值:'(1, 2, 3)',類(lèi)型:class 'str'> # 值:[1, 2, 3],類(lèi)型:class 'list'> # 值:{1, 2, 3},類(lèi)型:class 'set'>
如果一個(gè)2維元組遵循一定的規(guī)律,那么也可以將其轉(zhuǎn)換為字典類(lèi)型:
tup = (("k1", "v1"), ("k2", "v2"), ("k3", "v3")) dictTuple = dict(tup) print("值:%r,類(lèi)型:%r" % (dictTuple, type(dictTuple))) # 值:{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'},類(lèi)型:class 'dict'>
元組的索引操作僅支持獲取數(shù)據(jù)項(xiàng)。
其他的任意索引操作均不被支持。
使用方法參照列表的索引切片一節(jié)。
元組擁有絕對(duì)引用的特性,無(wú)論是深拷貝還是淺拷貝,都不會(huì)獲得其副本,而是直接對(duì)源對(duì)象進(jìn)行引用。
但是列表沒(méi)有絕對(duì)引用的特性,代碼驗(yàn)證如下:
>>> import copy >>> # 列表的深淺拷貝均創(chuàng)建新列表... >>> oldLi = [1, 2, 3] >>> id(oldLi) 4542649096 >>> li1 = copy.copy(oldLi) >>> id(li1) 4542648840 >>> li2 = copy.deepcopy(oldLi) >>> id(li2) 4542651208 >>> # 元組的深淺拷貝始終引用老元組 >>> oldTup = (1, 2, 3) >>> id(oldTup) 4542652920 >>> tup1 = copy.copy(oldTup) >>> id(tup1) 4542652920 >>> tup2 = copy.deepcopy(oldTup) >>> id(tup2) 4542652920
Python為何要這樣設(shè)計(jì)?其實(shí)仔細(xì)想想不難發(fā)現(xiàn),元組不能對(duì)其進(jìn)行操作,僅能獲取數(shù)據(jù)項(xiàng)。
那么也就沒(méi)有生成多個(gè)副本提供給開(kāi)發(fā)人員操作的必要了,因?yàn)槟阈薷牟涣嗽M,索性直接使用絕對(duì)引用策略。
值得注意的一點(diǎn):[:]也是淺拷貝,故對(duì)元組來(lái)說(shuō)屬于絕對(duì)引用范疇。
Leonardo Rochael在2013年的Python巴西會(huì)議提出了一個(gè)非常具有思考意義的問(wèn)題。
我們先來(lái)看一下:
>>> t = (1, 2, [30, 40]) >>> t[-1] += [50, 60] Traceback (most recent call last): File "stdin>", line 1, in module> TypeError: 'tuple' object does not support item assignment
現(xiàn)在,t到底會(huì)發(fā)生下面4種情況中的哪一種?
正確答案是4,t確實(shí)會(huì)變成 (1, 2, [30, 40, 50, 60]),但同時(shí)元組是不可變類(lèi)型故會(huì)引發(fā)TypeError異常的出現(xiàn)。
>>> t (1, 2, [30, 40, 50, 60])
如果是使用extend()對(duì)t[-1]的列表進(jìn)行數(shù)據(jù)項(xiàng)的增加,則答案會(huì)變成1。
我當(dāng)初在看了這個(gè)問(wèn)題后,暗自告訴自己了2件事情:
Ps:我也不知道自己為什么會(huì)產(chǎn)生這樣的想法,但這個(gè)想法確實(shí)伴隨我很長(zhǎng)時(shí)間,直至現(xiàn)在
元組更多的作用是展示數(shù)據(jù),而不是操作數(shù)據(jù)。
舉個(gè)例子,當(dāng)用戶根據(jù)某個(gè)操作獲取到了眾多數(shù)據(jù)項(xiàng)之后,你可以將這些數(shù)據(jù)項(xiàng)做出元組并返回。
用戶對(duì)被返回的原對(duì)象只能看,不能修改,若想修改則必須創(chuàng)建新其他類(lèi)型對(duì)象。
元組的解構(gòu)方法與列表使用相同。
使用方法參照列表的解構(gòu)方法一節(jié)。
方法一覽
常用的list方法一覽表:
方法名 | 返回值 | 描述 |
---|---|---|
count() | integer | 返回?cái)?shù)據(jù)項(xiàng)在T中出現(xiàn)的次數(shù) |
index() | integer | 返回第一個(gè)數(shù)據(jù)項(xiàng)在T中出現(xiàn)位置的索引,若值不存在,則拋出ValueError |
基礎(chǔ)公用函數(shù):
函數(shù)名 | 返回值 | 描述 |
---|---|---|
len() | integer | 返回容器中的項(xiàng)目數(shù) |
enumerate() | iterator for index, value of iterable | 返回一個(gè)可迭代對(duì)象,其中以小元組的形式包裹數(shù)據(jù)項(xiàng)與正向索引的對(duì)應(yīng)關(guān)系 |
reversed() | ... | 詳情參見(jiàn)函數(shù)章節(jié) |
sorted() | ... | 詳情參見(jiàn)函數(shù)章節(jié) |
使用len()方法來(lái)獲取元組的長(zhǎng)度。
返回int類(lèi)型的值。
tup = ("A", "B", "C", "D", "E", "F", "G") print(len(tup)) # 7
Python在對(duì)內(nèi)置的數(shù)據(jù)類(lèi)型使用len()方法時(shí),實(shí)際上是會(huì)直接的從PyVarObject結(jié)構(gòu)體中獲取ob_size屬性,這是一種非常高效的策略。
PyVarObject是表示內(nèi)存中長(zhǎng)度可變的內(nèi)置對(duì)象的C語(yǔ)言結(jié)構(gòu)體。
直接讀取這個(gè)值比調(diào)用一個(gè)方法要快很多。
使用count()方法統(tǒng)計(jì)數(shù)據(jù)項(xiàng)在該元組中出現(xiàn)的次數(shù)。
返回int:
tup = ("A", "B", "C", "D", "E", "F", "G", "A") aInTupCount = tup.count("A") print(aInTupCount) # 2
使用index()方法找到數(shù)據(jù)項(xiàng)在當(dāng)前元組中首次出現(xiàn)的位置索引值,如數(shù)據(jù)項(xiàng)不存在則拋出異常。
返回int。
tup = ("A", "B", "C", "D", "E", "F", "G", "A") aInTupIndex = tup.index("A") print(aInTupIndex) # 0
Python內(nèi)部實(shí)現(xiàn)中,列表和元組還是有一定的差別的。
元組在創(chuàng)建對(duì)象申請(qǐng)內(nèi)存的時(shí)候,內(nèi)存空間大小便進(jìn)行了固定,后續(xù)不可更改(如果是傳入了一個(gè)可迭代對(duì)象,例如tupe(range(100)),這種情況會(huì)進(jìn)行擴(kuò)容與縮容,下面的章節(jié)將進(jìn)行探討研究)。
而列表在創(chuàng)建對(duì)象申請(qǐng)內(nèi)存的時(shí)候,內(nèi)存空間大小不是固定的,如果后續(xù)對(duì)其新增或刪除數(shù)據(jù)項(xiàng),列表會(huì)進(jìn)行擴(kuò)容或者縮容機(jī)制。
空元組
若創(chuàng)建一個(gè)空元組,會(huì)直接進(jìn)行創(chuàng)建,然后將這個(gè)空元組丟到緩存free_list中。
元組的free_list最多能緩存 20 * 2000 個(gè)元組,這個(gè)在下面會(huì)進(jìn)行講解。
如圖所示:
這樣的代碼會(huì)進(jìn)行元組轉(zhuǎn)元組:
tup = tuple((1, 2, 3))
首先內(nèi)部本身就是一個(gè)元組(1, 2, 3),所以會(huì)直接將內(nèi)部的這個(gè)元組拿出來(lái)并返回引用,并不會(huì)再次創(chuàng)建。
代碼驗(yàn)證:
>>> oldTup = (1, 2, 3) >>> id(oldTup) 4384908128 >>> newTup = tuple(oldTup) >>> id(newTup) 4384908128 >>>
列表轉(zhuǎn)元組會(huì)將列表中的每一個(gè)數(shù)據(jù)項(xiàng)都拿出來(lái),然后放入至元組中:
tup = tuple([1, 2, 3])
所以你會(huì)發(fā)現(xiàn),列表和元組中的數(shù)據(jù)項(xiàng)引用都是相同的:
>>> li1 = ["A", "B", "C"] >>> tup = tuple(li1) >>> print(id(li1[0])) 4383760656 >>> print(id(tup[0])) 4383760656 >>>
可迭代對(duì)象是沒(méi)有長(zhǎng)度這一概念的,如果是可迭代對(duì)象轉(zhuǎn)換為元組,會(huì)先對(duì)可迭代對(duì)象的長(zhǎng)度做一個(gè)猜想。
并且根據(jù)這個(gè)猜想,為元組開(kāi)辟一片內(nèi)存空間,用于存放可迭代對(duì)象的數(shù)據(jù)項(xiàng)。
然后內(nèi)部會(huì)獲取可迭代對(duì)象的迭代器,對(duì)其進(jìn)行遍歷操作,拿出數(shù)據(jù)項(xiàng)后放至元組中。
如果猜想的長(zhǎng)度太小,會(huì)導(dǎo)致元組內(nèi)部的內(nèi)存不夠存放下所有的迭代器數(shù)據(jù)項(xiàng),此時(shí)該元組會(huì)進(jìn)行內(nèi)部的擴(kuò)容機(jī)制,直至可迭代對(duì)象中的數(shù)據(jù)項(xiàng)全部被添加至元組中。
rangeObject = range(1, 101) tup = tuple(rangeObject) // 假如猜想的是9 // 第一步:+ 10 // 第二步:+ (原長(zhǎng)度+10) * 0.25 // 其實(shí),就是增加【原長(zhǎng)度*0.25 + 2.5】
如果猜想的長(zhǎng)度太大,而實(shí)際上迭代器中的數(shù)據(jù)量偏少,則需要對(duì)該元組進(jìn)行縮容。
對(duì)元組進(jìn)行切片取值的時(shí)候,會(huì)開(kāi)辟一個(gè)新元組用于存放切片后得到的數(shù)據(jù)項(xiàng)。
tup = (1, 2, 3) newSliceTup = tup[0:2]
當(dāng)然,如果是[:]的操作,則參照絕對(duì)引用,直接返回被切片的元組引用。
代碼驗(yàn)證:
>>> id(tup) 4384908416 >>> newSliceTup = tup[0:2] >>> id(newSliceTup) 4384904392
free_list緩存
元組的緩存機(jī)制和列表的緩存機(jī)制不同。
元組的free_list會(huì)緩存0 - 19長(zhǎng)度的共20種元組,其中每一種長(zhǎng)度的元組通過(guò)單向鏈表橫向擴(kuò)展緩存至2000個(gè),如下圖所示:
當(dāng)每一次的del操作有數(shù)據(jù)項(xiàng)的元組時(shí),都會(huì)將該元組數(shù)據(jù)項(xiàng)清空并掛載至free_list單向鏈表的頭部的位置。
del 元組1 del 元組2 del 元組3
如下圖所示:
當(dāng)要?jiǎng)?chuàng)建一個(gè)元組時(shí),會(huì)通過(guò)創(chuàng)建元組的長(zhǎng)度,從free_list單向鏈表的頭部取出一個(gè)元組,然后將數(shù)據(jù)項(xiàng)存放進(jìn)去。
前提是free_list單向鏈表中緩存的有該長(zhǎng)度的元組。
tup = (1, 2, 3)
空元組的緩存是一經(jīng)創(chuàng)建就緩存到free_list單向鏈表中。
而非空元組的緩存必須是del操作后才緩存到free_list單向鏈表中。
第一次創(chuàng)建空元組后,空元組會(huì)緩存至free_list單向鏈表中。
以后的每一次空元組創(chuàng)建,返回的其實(shí)都是同一個(gè)引用,也就是說(shuō)空元組在free_list單向鏈表中即使被引用了也不會(huì)被銷(xiāo)毀。
>>> t1 = () >>> id(t1) 4511088712 >>> t2 = () >>> id(t2) 4511088712
當(dāng)free_list單向鏈表中有相同長(zhǎng)度的元組時(shí),會(huì)進(jìn)行引用并刪除。
這個(gè)在上圖中已經(jīng)示例過(guò)了,就是這個(gè):
代碼示例:
$ python3 Python 3.6.8 (v3.6.8:3c6b436a57, Dec 24 2018, 02:04:31) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> v1 = (None, None, None) >>> id(v1) 4384907696 >>> v2 = (None, None, None) >>> id(v2) 4384908056 >>> del v1 >>> del v2 # ① >>> v3 = (None, None, None) >>> id(v3) # ② 4384908056 >>> v4 = (None, None, None) >>> id(v4) # ③ 4384907696 >>>
①:free_list num_free=3 單向鏈表結(jié)構(gòu):v2 —> v1
②:創(chuàng)建了v3,拿出v2的空元組,填入v3數(shù)據(jù)項(xiàng),故v2和v3的id值相等,證明引用同一個(gè)元組,此時(shí)free_list num_free=3 單向鏈表結(jié)構(gòu)為:—> v1
③:創(chuàng)建了v4,拿出v1的空元組,填入v4數(shù)據(jù)項(xiàng),故v1和v4的id值相等,證明引用同一個(gè)元組
官網(wǎng)參考:點(diǎn)我跳轉(zhuǎn)
源碼一覽:點(diǎn)我跳轉(zhuǎn)
以下是截取了一些關(guān)鍵性源代碼,并且做上了中文注釋?zhuān)奖悴殚啞?/p>
每一個(gè)元組都有幾個(gè)關(guān)鍵性的屬性:
Py_ssize_t ob_refcnt; // 引用計(jì)數(shù)器 Py_ssize_t ob_size; // 數(shù)據(jù)項(xiàng)個(gè)數(shù),即元組大小 PyObject *ob_item[1]; // 存儲(chǔ)元組中的數(shù)據(jù)項(xiàng) [指針, ]
關(guān)于緩存free_list的屬性:
PyTuple_MAXSAVESIZE // 相當(dāng)于圖中的 free_num ,最大20,即縱向擴(kuò)展的緩存元組長(zhǎng)度 PyTuple_MAXFREELIST // 圖中 free_list 的橫向擴(kuò)展緩存列表個(gè)數(shù),最大2000
空元組
PyObject * PyTuple_New(Py_ssize_t size) { PyTupleObject *op; // 緩存相關(guān) Py_ssize_t i; // 元組的大小不能小于0 if (size 0) { PyErr_BadInternalCall(); return NULL; } #if PyTuple_MAXSAVESIZE > 0 // 創(chuàng)建空元組,優(yōu)先從緩存中獲取 // size = 0 表示這是一個(gè)空元組,從free_list[0]中獲取空元組 if (size == 0 free_list[0]) { // op就是空元組 op = free_list[0]; // 新增空元組引用計(jì)數(shù)器 + 1 Py_INCREF(op); #ifdef COUNT_ALLOCS tuple_zero_allocs++; #endif // 返回空元組的指針 return (PyObject *) op; } // 如果創(chuàng)建的不是空元組,且這個(gè)創(chuàng)建的元組數(shù)據(jù)項(xiàng)個(gè)數(shù)小于20,并且free_list[size]不等于空,表示有緩存 // 則從緩存中去獲取,不再重新開(kāi)辟內(nèi)存 if (size PyTuple_MAXSAVESIZE (op = free_list[size]) != NULL) { // 拿出元組 free_list[size] = (PyTupleObject *) op->ob_item[0]; // num_free減1 numfree[size]--; #ifdef COUNT_ALLOCS fast_tuple_allocs++; #endif /* Inline PyObject_InitVar */ // 初始化,定義這個(gè)元組的長(zhǎng)度為數(shù)據(jù)項(xiàng)個(gè)數(shù) #ifdef Py_TRACE_REFS Py_SIZE(op) = size; // 定義類(lèi)型為 tuple Py_TYPE(op) = PyTuple_Type; #endif // 增加一次新的引用 _Py_NewReference((PyObject *)op); } // 如果是空元組 else #endif { // 檢查內(nèi)存情況,是否充足 /* Check for overflow */ if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(PyTupleObject) - sizeof(PyObject *)) / sizeof(PyObject *)) { return PyErr_NoMemory(); } // 開(kāi)辟內(nèi)存,并獲得一個(gè)元組:op op = PyObject_GC_NewVar(PyTupleObject, PyTuple_Type, size); if (op == NULL) return NULL; } // 空元組的每一個(gè)槽位都是NULL for (i=0; i size; i++) op->ob_item[i] = NULL; #if PyTuple_MAXSAVESIZE > 0 // 緩存空元組 if (size == 0) { free_list[0] = op; ++numfree[0]; Py_INCREF(op); /* extra INCREF so that this is never freed */ } #endif #ifdef SHOW_TRACK_COUNT count_tracked++; #endif // 將元組加入到GC機(jī)制中,用于內(nèi)存管理 _PyObject_GC_TRACK(op); return (PyObject *) op; }
這個(gè)不在tupleobject.c源碼中,而是在abstract.c源碼中。
官網(wǎng)參考:點(diǎn)我跳轉(zhuǎn)
源碼一覽:點(diǎn)我跳轉(zhuǎn)
PyObject * PySequence_Tuple(PyObject *v) { PyObject *it; /* iter(v) */ Py_ssize_t n; /* guess for result tuple size */ PyObject *result = NULL; Py_ssize_t j; if (v == NULL) { return null_error(); } /* Special-case the common tuple and list cases, for efficiency. */ // 如果是元組轉(zhuǎn)換元組,如 tup = (1, 2, 3) 或者 tup = ((1, 2, 3))直接返回內(nèi)存地址 if (PyTuple_CheckExact(v)) { Py_INCREF(v); return v; } // 如果是列表轉(zhuǎn)換元組,則執(zhí)行PyList_AsTuple(),將列表轉(zhuǎn)換為元組 // 如 tup = ([1, 2, 3]) if (PyList_CheckExact(v)) return PyList_AsTuple(v); /* Get iterator. */ // 獲取迭代器, tup = (range(1, 4).__iter__()) it = PyObject_GetIter(v); if (it == NULL) return NULL; /* Guess result size and allocate space. */ // 猜想迭代器長(zhǎng)度,也就是猜一下有多少個(gè)數(shù)據(jù)項(xiàng) n = PyObject_LengthHint(v, 10); if (n == -1) goto Fail; // 根據(jù)猜想的迭代器長(zhǎng)度,進(jìn)行元組的內(nèi)存開(kāi)辟 result = PyTuple_New(n); if (result == NULL) goto Fail; /* Fill the tuple. */ // 將迭代器中每個(gè)數(shù)據(jù)項(xiàng)添加至元組中 for (j = 0; ; ++j) { PyObject *item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) goto Fail; break; } //如果迭代器中數(shù)據(jù)項(xiàng)比猜想的多,則證明開(kāi)辟內(nèi)存不足需要需要進(jìn)行擴(kuò)容 if (j >= n) { size_t newn = (size_t)n; /* The over-allocation strategy can grow a bit faster than for lists because unlike lists the over-allocation isn't permanent -- we reclaim the excess before the end of this routine. So, grow by ten and then add 25%. */ // 假如猜想的是9 // 第一步:+ 10 // 第二步:+ (原長(zhǎng)度+10) * 0.25 // 其實(shí),就是增加【原長(zhǎng)度*0.25 + 2.5】 newn += 10u; newn += newn >> 2; // 判斷是否超過(guò)了元組的數(shù)據(jù)項(xiàng)個(gè)數(shù)限制(sys.maxsize) if (newn > PY_SSIZE_T_MAX) { /* Check for overflow */ PyErr_NoMemory(); Py_DECREF(item); goto Fail; } n = (Py_ssize_t)newn; // 擴(kuò)容機(jī)制 if (_PyTuple_Resize(result, n) != 0) { Py_DECREF(item); goto Fail; } } // 將數(shù)據(jù)項(xiàng)放入元組之中 PyTuple_SET_ITEM(result, j, item); } /* Cut tuple back if guess was too large. */ // 如果猜想的數(shù)據(jù)項(xiàng)太多,而實(shí)際上迭代器中的數(shù)據(jù)量偏少 // 則需要對(duì)該元組進(jìn)行縮容 if (j n _PyTuple_Resize(result, j) != 0) goto Fail; Py_DECREF(it); return result; Fail: Py_XDECREF(result); Py_DECREF(it); return NULL; }
這個(gè)不在tupleobject.c源碼中,而是在listobject.c源碼中。
官網(wǎng)參考:點(diǎn)我跳轉(zhuǎn)
源碼一覽:點(diǎn)我跳轉(zhuǎn)
PyObject * PyList_AsTuple(PyObject *v) { PyObject *w; PyObject **p, **q; Py_ssize_t n; // 例如:tup = ([1, 2, 3]) // 進(jìn)行列表的驗(yàn)證 if (v == NULL || !PyList_Check(v)) { PyErr_BadInternalCall(); return NULL; } // 獲取大小,即數(shù)據(jù)項(xiàng)個(gè)數(shù) n = Py_SIZE(v); // 開(kāi)辟內(nèi)存 w = PyTuple_New(n); // 如果是空元組 if (w == NULL) return NULL; // 執(zhí)行遷徙操作 p = ((PyTupleObject *)w)->ob_item; q = ((PyListObject *)v)->ob_item; // 將列表中數(shù)據(jù)項(xiàng)的引用,也給元組進(jìn)行引用 // 這樣列表中數(shù)據(jù)項(xiàng)和元組中的數(shù)據(jù)項(xiàng)都引用同1個(gè)對(duì)象 while (--n >= 0) { // 數(shù)據(jù)項(xiàng)引用計(jì)數(shù) + 1 Py_INCREF(*q); *p = *q; p++; q++; } // 返回元組 return w; }
PyObject * PyTuple_GetSlice(PyObject *op, Py_ssize_t i, Py_ssize_t j) // 切片會(huì)觸發(fā)該方法 { // 如果對(duì)空元組進(jìn)行切片,則會(huì)拋出異常 if (op == NULL || !PyTuple_Check(op)) { PyErr_BadInternalCall(); return NULL; } // 內(nèi)部的具體實(shí)現(xiàn)方法 return tupleslice((PyTupleObject *)op, i, j); } static PyObject * tupleslice(PyTupleObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) { PyTupleObject *np; PyObject **src, **dest; Py_ssize_t i; Py_ssize_t len; // 計(jì)算索引位置 if (ilow 0) ilow = 0; if (ihigh > Py_SIZE(a)) ihigh = Py_SIZE(a); if (ihigh ilow) ihigh = ilow; // 如果是[:]的操作,則直接返回源元組對(duì)象a的指針,即絕對(duì)引用 if (ilow == 0 ihigh == Py_SIZE(a) PyTuple_CheckExact(a)) { Py_INCREF(a); return (PyObject *)a; } // 初始化新的切片對(duì)象元組長(zhǎng)度 len = ihigh - ilow; // 開(kāi)始切片,創(chuàng)建了一個(gè)新元組np np = (PyTupleObject *)PyTuple_New(len); if (np == NULL) return NULL; src = a->ob_item + ilow; dest = np->ob_item; // 對(duì)源元組中的數(shù)據(jù)項(xiàng)的引用計(jì)數(shù)+1 for (i = 0; i len; i++) { PyObject *v = src[i]; Py_INCREF(v); dest[i] = v; } // 返回切片對(duì)象新元組np的引用 return (PyObject *)np; }
static void tupledealloc(PyTupleObject *op) { Py_ssize_t i; Py_ssize_t len = Py_SIZE(op); PyObject_GC_UnTrack(op); Py_TRASHCAN_SAFE_BEGIN(op) // 如果元組的長(zhǎng)度大于0,則不是一個(gè)非空元組 if (len > 0) { i = len; // 將內(nèi)部的數(shù)據(jù)項(xiàng)引用計(jì)數(shù)都 - 1 while (--i >= 0) Py_XDECREF(op->ob_item[i]); #if PyTuple_MAXSAVESIZE > 0 // 準(zhǔn)備緩存,判斷num_free是否小于20,并且單向鏈表中的已緩存元組個(gè)數(shù)小于2000 if (len PyTuple_MAXSAVESIZE numfree[len] PyTuple_MAXFREELIST Py_TYPE(op) == PyTuple_Type) { // 添加至鏈表頭部 op->ob_item[0] = (PyObject *) free_list[len]; // 將num_free + 1 numfree[len]++; free_list[len] = op; goto done; /* return */ } #endif } // 內(nèi)存中進(jìn)行銷(xiāo)毀 Py_TYPE(op)->tp_free((PyObject *)op); done: Py_TRASHCAN_SAFE_END(op) }
以上就是老Python帶你從淺入深探究Tuple的詳細(xì)內(nèi)容,更多關(guān)于Python Tuple的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
標(biāo)簽:黔西 惠州 鷹潭 黑龍江 益陽(yáng) 四川 上海 常德
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Python帶你從淺入深探究Tuple(基礎(chǔ)篇)》,本文關(guān)鍵詞 Python,帶你,從,淺入,深,探究,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。