主頁 > 知識(shí)庫 > Python帶你從淺入深探究Tuple(基礎(chǔ)篇)

Python帶你從淺入深探究Tuple(基礎(chǔ)篇)

熱門標(biāo)簽:騰訊外呼線路 激戰(zhàn)2地圖標(biāo)注 陜西金融外呼系統(tǒng) 唐山智能外呼系統(tǒng)一般多少錢 海南400電話如何申請(qǐng) 白銀外呼系統(tǒng) 公司電話機(jī)器人 哈爾濱ai外呼系統(tǒng)定制 廣告地圖標(biāo)注app

元組

Python中的元組容器序列(tuple)與列表容器序列(list)具有極大的相似之處,因此也常被稱為不可變的列表。

但是兩者之間也有很多的差距,元組側(cè)重于數(shù)據(jù)的展示,而列表側(cè)重于數(shù)據(jù)的存儲(chǔ)與操作。

它們非常相似,雖然都可以存儲(chǔ)任意類型的數(shù)據(jù),但是一個(gè)元組定義好之后就不能夠再進(jìn)行修改。

元組特性

元組的特點(diǎn):

  • 元組屬于容器序列
  • 元組屬于不可變類型
  • 元組底層由順序存儲(chǔ)組成,而順序存儲(chǔ)是線性結(jié)構(gòu)的一種

基本聲明

以下是使用類實(shí)例化的形式進(jìn)行對(duì)象聲明:

tup = tuple((1, 2, 3, 4, 5))
print("值:%r,類型:%r" % (tup, type(tup)))

# 值:(1, 2, 3, 4, 5),類型:class 'tuple'>

也可以選擇使用更方便的字面量形式進(jìn)行對(duì)象聲明,使用逗號(hào)對(duì)數(shù)據(jù)項(xiàng)之間進(jìn)行分割:

tup = 1, 2, 3, 4, 5
print("值:%r,類型:%r" % (tup, type(tup)))

# 值:(1, 2, 3, 4, 5),類型:class 'tuple'>

為了美觀,我們一般會(huì)在兩側(cè)加上(),但是要確定一點(diǎn),元組定義是逗號(hào)分隔的數(shù)據(jù)項(xiàng),而并非是()包裹的數(shù)據(jù)項(xiàng):

tup = (1, 2, 3, 4, 5)
print("值:%r,類型:%r" % (tup, type(tup)))

# 值:(1, 2, 3, 4, 5),類型:class 'tuple'>

多維元組

當(dāng)一個(gè)元組中嵌套另一個(gè)元組,該元組就可以稱為多維元組。

如下,定義一個(gè)2維元組:

tup = (1, 2, 3, 4, 5)
print("值:%r,類型:%r" % (tup, type(tup)))

# 值:(1, 2, 3, 4, 5),類型:class 'tuple'>

續(xù)行操作

在Python中,元組中的數(shù)據(jù)項(xiàng)如果過多,可能會(huì)導(dǎo)致整個(gè)元組太長,太長的元組是不符合PEP8規(guī)范的。

每行最大的字符數(shù)不可超過79,文檔字符或者注釋每行不可超過72

Python雖然提供了續(xù)行符\,但是在元組中可以忽略續(xù)行符,如下所示:

tup = (1, 2, ("三", "四"))
print("值:%r,類型:%r" % (tup, type(tup)))

# 值:(1, 2, ('三', '四')),類型:class 'tuple'>

類型轉(zhuǎn)換

元組支持與布爾型、字符串、列表、以及集合類型進(jìn)行類型轉(zhuǎn)換:

tup = (1, 2, 3)
bTup = bool(tup)    # 布爾類型
strTup = str(tup)   # 字符串類型
liTup = list(tup)   # 列表類型
setTup = set(tup)   # 集合類型

print("值:%r,類型:%r" % (bTup, type(bTup)))
print("值:%r,類型:%r" % (strTup, type(strTup)))
print("值:%r,類型:%r" % (liTup, type(liTup)))
print("值:%r,類型:%r" % (setTup, type(setTup)))

# 值:True,類型:class 'bool'>
# 值:'(1, 2, 3)',類型:class 'str'>
# 值:[1, 2, 3],類型:class 'list'>
# 值:{1, 2, 3},類型:class 'set'>

如果一個(gè)2維元組遵循一定的規(guī)律,那么也可以將其轉(zhuǎn)換為字典類型:

tup = (("k1", "v1"), ("k2", "v2"), ("k3", "v3"))
dictTuple = dict(tup)

print("值:%r,類型:%r" % (dictTuple, type(dictTuple)))

# 值:{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'},類型:class 'dict'>

索引操作

元組的索引操作僅支持獲取數(shù)據(jù)項(xiàng)。

其他的任意索引操作均不被支持。

使用方法參照列表的索引切片一節(jié)。

絕對(duì)引用

元組擁有絕對(duì)引用的特性,無論是深拷貝還是淺拷貝,都不會(huì)獲得其副本,而是直接對(duì)源對(duì)象進(jìn)行引用。

但是列表沒有絕對(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)。

那么也就沒有生成多個(gè)副本提供給開發(fā)人員操作的必要了,因?yàn)槟阈薷牟涣嗽M,索性直接使用絕對(duì)引用策略。

值得注意的一點(diǎn):[:]也是淺拷貝,故對(duì)元組來說屬于絕對(duì)引用范疇。

元組的陷阱

Leonardo Rochael在2013年的Python巴西會(huì)議提出了一個(gè)非常具有思考意義的問題。

我們先來看一下:

>>> 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種情況中的哪一種?

  1. t 變成 (1, 2, [30, 40, 50, 60])。
  2. 因?yàn)?tuple 不支持對(duì)它的數(shù)據(jù)項(xiàng)賦值,所以會(huì)拋出 TypeError 異常。
  3. 以上兩個(gè)都不是。a 和 b 都是對(duì)的。

正確答案是4,t確實(shí)會(huì)變成 (1, 2, [30, 40, 50, 60]),但同時(shí)元組是不可變類型故會(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è)問題后,暗自告訴自己了2件事情:

  • list的數(shù)據(jù)項(xiàng)增加盡量不要使用+=,而應(yīng)該使用append()或者extend()

Ps:我也不知道自己為什么會(huì)產(chǎn)生這樣的想法,但這個(gè)想法確實(shí)伴隨我很長時(shí)間,直至現(xiàn)在

  • tuple中不要存放可變類型的數(shù)據(jù),如list、set、dict等..

元組更多的作用是展示數(shù)據(jù),而不是操作數(shù)據(jù)。

舉個(gè)例子,當(dāng)用戶根據(jù)某個(gè)操作獲取到了眾多數(shù)據(jù)項(xiàng)之后,你可以將這些數(shù)據(jù)項(xiàng)做出元組并返回。

用戶對(duì)被返回的原對(duì)象只能看,不能修改,若想修改則必須創(chuàng)建新其他類型對(duì)象。

解構(gòu)方法

元組的解構(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() ... 詳情參見函數(shù)章節(jié)
sorted() ... 詳情參見函數(shù)章節(jié)

獲取長度

使用len()方法來獲取元組的長度。

返回int類型的值。

tup = ("A", "B", "C", "D", "E", "F", "G")

print(len(tup))

# 7

Python在對(duì)內(nèi)置的數(shù)據(jù)類型使用len()方法時(shí),實(shí)際上是會(huì)直接的從PyVarObject結(jié)構(gòu)體中獲取ob_size屬性,這是一種非常高效的策略。

PyVarObject是表示內(nèi)存中長度可變的內(nèi)置對(duì)象的C語言結(jié)構(gòu)體。

直接讀取這個(gè)值比調(diào)用一個(gè)方法要快很多。

統(tǒng)計(jì)次數(shù)

使用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

底層探究

內(nèi)存開辟

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)建

空元組

若創(chuàng)建一個(gè)空元組,會(huì)直接進(jìn)行創(chuàng)建,然后將這個(gè)空元組丟到緩存free_list中。

元組的free_list最多能緩存 20 * 2000 個(gè)元組,這個(gè)在下面會(huì)進(jìn)行講解。

如圖所示:

元組轉(zhuǎn)元組

這樣的代碼會(huì)進(jìn)行元組轉(zhuǎn)元組:

tup = tuple((1, 2, 3))

首先內(nèi)部本身就是一個(gè)元組(1, 2, 3),所以會(huì)直接將內(nèi)部的這個(gè)元組拿出來并返回引用,并不會(huì)再次創(chuàng)建。

代碼驗(yàn)證:

>>> oldTup = (1, 2, 3)
>>> id(oldTup)
4384908128
>>> newTup = tuple(oldTup)
>>> id(newTup)
4384908128
>>>

列表轉(zhuǎn)元組

列表轉(zhuǎn)元組會(huì)將列表中的每一個(gè)數(shù)據(jù)項(xiàng)都拿出來,然后放入至元組中:

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ì)象轉(zhuǎn)元組

可迭代對(duì)象是沒有長度這一概念的,如果是可迭代對(duì)象轉(zhuǎn)換為元組,會(huì)先對(duì)可迭代對(duì)象的長度做一個(gè)猜想。

并且根據(jù)這個(gè)猜想,為元組開辟一片內(nèi)存空間,用于存放可迭代對(duì)象的數(shù)據(jù)項(xiàng)。

然后內(nèi)部會(huì)獲取可迭代對(duì)象的迭代器,對(duì)其進(jìn)行遍歷操作,拿出數(shù)據(jù)項(xià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 
// 第二步:+ (原長度+10) * 0.25
// 其實(shí),就是增加【原長度*0.25 + 2.5】

如果猜想的長度太大,而實(shí)際上迭代器中的數(shù)據(jù)量偏少,則需要對(duì)該元組進(jìn)行縮容。

切片取值

對(duì)元組進(jìn)行切片取值的時(shí)候,會(huì)開辟一個(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

緩存機(jī)制

free_list緩存

元組的緩存機(jī)制和列表的緩存機(jī)制不同。

元組的free_list會(huì)緩存0 - 19長度的共20種元組,其中每一種長度的元組通過單向鏈表橫向擴(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ì)通過創(chuàng)建元組的長度,從free_list單向鏈表的頭部取出一個(gè)元組,然后將數(shù)據(jù)項(xiàng)存放進(jìn)去。

前提是free_list單向鏈表中緩存的有該長度的元組。

tup = (1, 2, 3)

空元組與非空元組的緩存

空元組的緩存是一經(jīng)創(chuàng)建就緩存到free_list單向鏈表中。

而非空元組的緩存必須是del操作后才緩存到free_list單向鏈表中。

空元組的創(chuàng)建

第一次創(chuàng)建空元組后,空元組會(huì)緩存至free_list單向鏈表中。

以后的每一次空元組創(chuàng)建,返回的其實(shí)都是同一個(gè)引用,也就是說空元組在free_list單向鏈表中即使被引用了也不會(huì)被銷毀。

>>> t1 = ()
>>> id(t1)
4511088712
>>> t2 = ()
>>> id(t2)
4511088712

非空元組的創(chuàng)建

當(dāng)free_list單向鏈表中有相同長度的元組時(shí),會(huì)進(jìn)行引用并刪除。

這個(gè)在上圖中已經(jīng)示例過了,就是這個(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è)元組

tupleobject.c源碼

官網(wǎng)參考:點(diǎn)我跳轉(zhuǎn)

源碼一覽:點(diǎn)我跳轉(zhuǎn)

以下是截取了一些關(guān)鍵性源代碼,并且做上了中文注釋,方便查閱。

每一個(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ò)展的緩存元組長度
PyTuple_MAXFREELIST     // 圖中 free_list 的橫向擴(kuò)展緩存列表個(gè)數(shù),最大2000

創(chuàng)建元組

空元組

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]不等于空,表示有緩存
    // 則從緩存中去獲取,不再重新開辟內(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è)元組的長度為數(shù)據(jù)項(xiàng)個(gè)數(shù)
#ifdef Py_TRACE_REFS
        Py_SIZE(op) = size;
        // 定義類型為 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();
        }
        // 開辟內(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;
}

可迭代對(duì)象轉(zhuǎn)元組

這個(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. */
    // 猜想迭代器長度,也就是猜一下有多少個(gè)數(shù)據(jù)項(xiàng)
    n = PyObject_LengthHint(v, 10);
    if (n == -1)
        goto Fail;
        
    // 根據(jù)猜想的迭代器長度,進(jìn)行元組的內(nè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)比猜想的多,則證明開辟內(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 
            // 第二步:+ (原長度+10) * 0.25
            // 其實(shí),就是增加【原長度*0.25 + 2.5】
            
            newn += 10u;
            newn += newn >> 2;
            
            // 判斷是否超過了元組的數(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;
}

列表轉(zhuǎn)元組

這個(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);
    // 開辟內(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ì)象元組長度
    len = ihigh - ilow;
    
    // 開始切片,創(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;
}

緩存相關(guān)

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)
    
    // 如果元組的長度大于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)行銷毀
    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)文章!

您可能感興趣的文章:
  • Python中的list與tuple集合區(qū)別解析
  • python3 字符串/列表/元組(str/list/tuple)相互轉(zhuǎn)換方法及join()函數(shù)的使用
  • python 提取tuple類型值中json格式的key值方法
  • python: 判斷tuple、list、dict是否為空的方法
  • 詳談Python中列表list,元祖tuple和numpy中的array區(qū)別
  • Python中內(nèi)置數(shù)據(jù)類型list,tuple,dict,set的區(qū)別和用法
  • Python 元組(Tuple)操作詳解

標(biāo)簽:黔西 惠州 鷹潭 黑龍江 益陽 四川 上海 常德

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Python帶你從淺入深探究Tuple(基礎(chǔ)篇)》,本文關(guān)鍵詞  Python,帶你,從,淺入,深,探究,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Python帶你從淺入深探究Tuple(基礎(chǔ)篇)》相關(guān)的同類信息!
  • 本頁收集關(guān)于Python帶你從淺入深探究Tuple(基礎(chǔ)篇)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章

    上一篇:Python中zipfile壓縮包模塊的使用

    下一篇:Python 高級(jí)庫15 個(gè)讓新手愛不釋手(推薦)