簡體   English   中英

列表和元組有什么區別?

[英]What's the difference between lists and tuples?

元組/列表之間有什么區別,它們的優點/缺點是什么?

除了元組是不可變的之外,還有一個語義區別應該指導它們的使用。 元組是異構數據結構(即它們的條目具有不同的含義),而列表是同構序列。 元組有結構,列表有順序。

使用這種區別使代碼更加明確和易於理解。

一個例子是成對的頁碼和行號來引用書中的位置,例如:

my_location = (42, 11)  # page number, line number

然后,您可以將其用作字典中的鍵來存儲位置上的注釋。 另一方面,列表可用於存儲多個位置。 自然地,人們可能想要在列表中添加或刪除位置,因此列表是可變的是有道理的。 另一方面,從現有位置添加或刪除項目沒有意義 - 因此元組是不可變的。

在某些情況下,您可能想要更改現有位置元組中的項目,例如在遍歷頁面的行時。 但是元組不變性迫使您為每個新值創建一個新的位置元組。 從表面上看,這似乎很不方便,但使用像這樣的不可變數據是值類型和函數式編程技術的基石,可以有很大的優勢。

關於這個問題有一些有趣的文章,例如“Python 元組不僅僅是常量列表”“理解元組與 Python 中的列表” Python 官方文檔也提到了這一點

“元組是不可變的,通常包含異構序列......”。

在像Haskell這樣的靜態類型語言中,元組中的值通常具有不同的類型,並且元組的長度必須是固定的。 在列表中,值都具有相同的類型,並且長度不固定。 所以區別非常明顯。

最后是Python中的 namedtuple,這是有道理的,因為元組已經應該具有結構。 這強調了元組是類和實例的輕量級替代方案。

列表和元組的區別

  1. 文字

    someTuple = (1,2) someList = [1,2]
  2. 尺寸

    a = tuple(range(1000)) b = list(range(1000)) a.__sizeof__() # 8024 b.__sizeof__() # 9088

    由於元組操作的尺寸較小,它會變得更快一些,但在您擁有大量元素之前,無需過多提及。

  3. 允許的操作

    b = [1,2] b[0] = 3 # [3, 2] a = (1,2) a[0] = 3 # Error

    這也意味着您不能刪除元素或對元組進行排序。 但是,您可以向列表和元組添加一個新元素,唯一的區別是,由於元組是不可變的,因此您並沒有真正添加元素,而是創建了一個新元組,因此 id 會改變

    a = (1,2) b = [1,2] id(a) # 140230916716520 id(b) # 748527696 a += (3,) # (1, 2, 3) b += [3] # [1, 2, 3] id(a) # 140230916878160 id(b) # 748527696
  4. 用法

    由於列表是可變的,它不能用作字典中的鍵,而可以使用元組。

     a = (1,2) b = [1,2] c = {a: 1} # OK c = {b: 1} # Error

如果您去散步,您可以隨時在(x,y)元組中記錄您的坐標。

如果你想記錄你的旅程,你可以每隔幾秒把你的位置記錄到一個列表中。

但是你不能反過來做。

關鍵區別在於元組是不可變的。 這意味着一旦創建元組,就無法更改元組中的值。

因此,如果您需要更改值,請使用列表。

元組的好處:

  1. 性能略有提升。
  2. 由於元組是不可變的,它可以用作字典中的鍵。
  3. 如果你不能改變它,其他人也不能,也就是說你不需要擔心任何 API 函數等會在不被詢問的情況下改變你的元組。

列表是可變的; 元組不是。

來自docs.python.org/2/tutorial/datastructures.html

元組是不可變的,通常包含一個異構的元素序列,這些元素通過解包(見本節后面部分)或索引(甚至在命名元組的情況下通過屬性)訪問。 列表是可變的,它們的元素通常是同質的,可以通過遍歷列表來訪問。

有人提到,區別主要是語義上的:人們期望一個元組和一個列表來表示不同的信息。 但這不僅僅是指導方針; 一些庫實際上根據它們傳遞的內容而表現不同。 以 NumPy 為例(從我要求更多示例的另一篇文章中復制):

>>> import numpy as np
>>> a = np.arange(9).reshape(3,3)
>>> a
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> idx = (1,1)
>>> a[idx]
4
>>> idx = [1,1]
>>> a[idx]
array([[3, 4, 5],
       [3, 4, 5]])

關鍵是,雖然 NumPy 可能不是標准庫的一部分,但它是主要的 Python 庫,而在 NumPy 中,列表和列表完全不同。

這是 Python 列表的示例:

my_list = [0,1,2,3,4]
top_rock_list = ["Bohemian Rhapsody","Kashmir","Sweet Emotion", "Fortunate Son"]

這是 Python 元組的示例:

my_tuple = (a,b,c,d,e)
celebrity_tuple = ("John", "Wayne", 90210, "Actor", "Male", "Dead")

Python 列表和元組的相似之處在於它們都是有序的 collections 值。 除了使用括號“[..., ... ]”創建列表和使用括號“(..., ... )”創建元組的細微區別之外,它們之間的核心技術“以 Python 語法硬編碼”的區別是一個特定元組的元素是不可變的,而列表是可變的(......所以只有元組是可散列的並且可以用作字典/散列鍵。),這會導致它們如何使用或不能使用的差異(通過語法先驗地強制執行)以及人們選擇使用它們的方式的差異(鼓勵作為“最佳實踐”,后驗,這就是聰明的程序員所做的)。 區分何時使用元組與何時使用列表的主要區別在於人們賦予元素順序的含義

對於元組,“順序”僅表示用於保存信息的特定“結構”。 在第一個字段中找到的值可以輕松切換到第二個字段,因為每個字段都提供跨兩個不同維度或比例的值。 它們為不同類型的問題提供答案,通常具有以下形式:對於給定的對象/主題,它的屬性是什么? 對象/主體保持不變,屬性不同。

對於列表,“順序”表示順序或方向性。 第二個元素必須在第一個元素之后,因為它基於特定和常見的比例或尺寸位於第二位。 這些元素被視為一個整體,並且主要為通常形式的單個問題提供答案,對於給定的屬性,這些對象/主題如何比較? 屬性保持不變,對象/主題不同。

流行文化中的人和程序員不符合這些差異的例子不勝枚舉,也有不計其數的人可能會在主菜中使用沙拉叉。 歸根結底,這很好,兩者通常都能完成工作。

總結一些更精細的細節

相似之處:

  1. 重復- 元組和列表都允許重復
  2. 索引、選擇和切片- 元組和列表都使用括號內的 integer 值進行索引。 因此,如果您想要給定列表或元組的前 3 個值,語法將是相同的:

     >>> my_list[0:3] [0,1,2] >>> my_tuple[0:3] [a,b,c]
  3. 比較和排序- 兩個元組或兩個列表都通過它們的第一個元素進行比較,如果存在平局,則通過第二個元素進行比較,依此類推。 在前面的元素表現出差異之后,不再關注后面的元素。

     >>> [0,2,0,0,0,0]>[0,0,0,0,0,500] True >>> (0,2,0,0,0,0)>(0,0,0,0,0,500) True

差異: - 先驗,根據定義

  1. 語法- 列表使用 [],元組使用 ()

  2. 可變性- 給定列表中的元素是可變的,給定元組中的元素是不可變的。

     # Lists are mutable: >>> top_rock_list ['Bohemian Rhapsody', 'Kashmir', 'Sweet Emotion', 'Fortunate Son'] >>> top_rock_list[1] 'Kashmir' >>> top_rock_list[1] = "Stairway to Heaven" >>> top_rock_list ['Bohemian Rhapsody', 'Stairway to Heaven', 'Sweet Emotion', 'Fortunate Son'] # Tuples are NOT mutable: >>> celebrity_tuple ('John', 'Wayne', 90210, 'Actor', 'Male', 'Dead') >>> celebrity_tuple[5] 'Dead' >>> celebrity_tuple[5]="Alive" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment
  3. Hashtables (Dictionaries) - 由於 hashtables (dictionaries) 要求其鍵是可散列的,因此是不可變的,因此只有元組可以充當字典鍵,而不是列表。

     #Lists CAN'T act as keys for hashtables(dictionaries) >>> my_dict = {[a,b,c]:"some value"} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' #Tuples CAN act as keys for hashtables(dictionaries) >>> my_dict = {("John","Wayne"): 90210} >>> my_dict {('John', 'Wayne'): 90210}

差異 - 使用中的后驗

  1. Homo vs. Heterogeneity of Elements - 通常列表對象是同質的,而元組對象是異構的。 也就是說,列表用於相同類型的對象/主題(如所有總統候選人,或所有歌曲,或所有跑步者),盡管它不是強制的),而元組更多用於異構對象。

  2. 循環與結構 - 雖然兩者都允許循環(對於 my_list 中的 x...),但對列表執行此操作才真正有意義。 元組更適合結構化和呈現信息(位於 %s 中的 %s %s 是 %s,現在是 %s % ("John","Wayne",90210, "Actor","Dead"))

列表用於循環,元組用於結構,即"%s %s" %tuple

列表通常是同質的,元組通常是異構的。

列表用於可變長度,元組用於固定長度。

列表的值可以隨時更改,但元組的值不能更改。

優點和缺點取決於用途。 如果你有這樣一個你不想改變的數據,那么你應該使用元組,否則列表是最好的選擇。

列表和元組的區別

元組和列表都是 Python 中看似相似的序列類型。

  1. 字面量語法

    我們使用括號 ( ) 構造元組和方括號[ ]以獲取新列表。 此外,我們可以使用適當類型的調用來獲取所需的結構——元組或列表。

     someTuple = (4,6) someList = [2,6]
  2. 可變性

    元組是不可變的,而列表是可變的。 這一點是以下幾點的基礎。

  3. Memory 用法

    由於可變性,您需要更多的 memory 用於列表和更少的 memory 用於元組。

  4. 擴展

    您可以將新元素添加到元組和列表中,唯一的區別是元組的 id 將被更改(即,我們將擁有一個新對象)。

  5. 散列

    元組是可散列的,而列表則不是。 這意味着您可以將元組用作字典中的鍵。 列表不能用作字典中的鍵,而可以使用元組

    tup = (1,2) list_ = [1,2] c = {tup: 1} # ok c = {list_: 1} # error
  6. 語義

    這一點更多的是關於最佳實踐。 您應該將元組用作異構數據結構,而列表是同質序列。

列表旨在成為同質序列,而元組是異構數據結構。

正如人們已經在這里回答的那樣, tuples是不可變的,而lists是可變的,但是我們必須記住使用元組的一個重要方面

如果tuple中包含一個listdictionary ,即使tuple本身是不可變的,這些也可以更改。

例如,假設我們有一個包含列表和字典的元組

my_tuple = (10,20,30,[40,50],{ 'a' : 10})

我們可以將列表的內容更改為

my_tuple[3][0] = 400
my_tuple[3][1] = 500

這使得新元組看起來像

(10, 20, 30, [400, 500], {'a': 10})

我們還可以將元組內的字典更改為

my_tuple[4]['a'] = 500

這將使整個元組看起來像

(10, 20, 30, [400, 500], {'a': 500})

發生這種情況是因為listdictionary是對象,這些對象並沒有改變,而是它指向的內容。

所以tuple毫無例外地保持不變

PEP 484 -- Type Hints表示tuple的元素類型可以單獨鍵入; 這樣你就可以說Tuple[str, int, float] ; 但是listList類型 class 只能采用一個類型參數: List[str] ,這暗示 2 的區別實際上是前者是異構的,而后者本質上是同質的。

此外,標准庫主要使用元組作為此類標准函數的返回值,其中 C 將返回一個struct

正如人們已經提到的差異,我將寫下為什么要使用元組。

為什么首選元組?

小元組的分配優化

為了減少 memory 碎片並加快分配速度,Python 重用了舊元組。 如果一個元組不再需要並且少於 20 個項目而不是永久刪除它 Python 將其移動到空閑列表。

一個空閑列表分為 20 個組,每個組代表一個長度為 0 到 20 之間的元組列表。每個組最多可以存儲 2000 個元組。 第一個(零)組僅包含 1 個元素並表示一個空元組。

>>> a = (1,2,3)
>>> id(a)
4427578104
>>> del a
>>> b = (1,2,4)
>>> id(b)
4427578104

在上面的示例中,我們可以看到 a 和 b 具有相同的 id。 那是因為我們立即占用了空閑列表上的已銷毀元組。

列表的分配優化

由於可以修改列表,因此 Python 不使用與元組相同的優化。 但是,Python 列表也有空閑列表,但它只用於空對象。 如果空列表被 GC 刪除或收集,以后可以重復使用。

>>> a = []
>>> id(a)
4465566792
>>> del a
>>> b = []
>>> id(b)
4465566792

資料來源: https://rushter.com/blog/python-lists-and-tuples/

為什么元組比列表有效? -> https://stackoverflow.com/a/22140115

最重要的區別是時間! 當您不想更改列表內的數據時,最好使用元組! 這是為什么使用元組的示例!

import timeit
print(timeit.timeit(stmt='[1,2,3,4,5,6,7,8,9,10]', number=1000000)) #created list
print(timeit.timeit(stmt='(1,2,3,4,5,6,7,8,9,10)', number=1000000)) # created tuple 

在這個例子中,我們執行了這兩個語句 100 萬次

Output:

0.136621
0.013722200000000018

任何人都可以清楚地注意到時差。

來自5.3 文檔的方向引用。 元組和序列

盡管元組可能看起來類似於列表,但它們通常用於不同的情況和不同的目的。 元組是不可變的,通常包含一個異構的元素序列,這些元素通過解包(見本節后面)或索引(甚至在命名元組的情況下通過屬性)訪問。 列表是可變的,它們的元素通常是同質的,可以通過遍歷列表來訪問。

換句話說,元組用於存儲元素組,其中組的內容/成員不會更改,而列表用於存儲元素組,其中組的成員可以更改。

例如,如果我想將 my.network 的 IP 存儲在一個變量中,我最好使用一個元組,因為 IP 是固定的。 像這樣my_ip = ('192.168.0.15', 33, 60) 但是,如果我想存儲我將在接下來的 6 個月內訪問的地點的 IP 組,那么我應該使用 LIST,因為我會不斷更新並向組中添加新的 IP。 像這樣

places_to_visit = [
    ('192.168.0.15', 33, 60), 
    ('192.168.0.22', 34, 60), 
    ('192.168.0.1', 34, 60), 
    ('192.168.0.2', 34, 60), 
    ('192.168.0.8', 34, 60), 
    ('192.168.0.11', 34, 60)
] 

首先,它們都是Python中的非標量對象(也稱為復合對象)。

  • 元組,元素的有序序列(可以包含任何 object,沒有別名問題)
    • 不可變(元組、int、float、str)
    • 使用+連接(當然會創建全新的元組)
    • 索引
    • 切片
    • Singleton (3,) # -> (3)而不是(3) # -> 3
  • 列表(其他語言的數組),有序的值序列
    • 可變的
    • Singleton [3]
    • 克隆new_array = origin_array[:]
    • 列表理解[x**2 for x in range(1,7)]為您提供[1,4,9,16,25,36] (不可讀)

使用 list 也可能導致別名錯誤(兩個不同的路徑指向同一個對象)。

只是listtuple響應的快速擴展:

  • 由於動態特性, list分配的比特桶比實際所需的 memory 多。 這樣做是為了防止在將來附加額外項目時進行昂貴的重新分配操作。

  • 另一方面,作為 static,輕量級tuple object 不保留存儲它們所需的額外 memory。

列表是可變的,元組是不可變的。 只要考慮這個例子。

a = ["1", "2", "ra", "sa"]    #list
b = ("1", "2", "ra", "sa")    #tuple

現在更改列表和元組的索引值。

a[2] = 1000
print a     #output : ['1', '2', 1000, 'sa']
b[2] = 1000
print b     #output : TypeError: 'tuple' object does not support item assignment.

因此證明以下代碼對元組無效,因為我們試圖更新一個元組,這是不允許的。

列表是可變的。 而元組是不可變的。 在元組中使用索引訪問偏移元素比列表更有意義,因為元素及其索引不能更改。

除了這里提出的許多其他評論外,我看到使用元組的好處是它們能夠具有不同類型的值而不像列表的靈活性。

以一個數據庫表為例,它為每一列指定了不同的值和類型。 一個列表根本無法復制這一點(因為它限制為它可以包含的單一類型的值),而元組可以有多個不同的類型和值,它們的位置在每列中都得到尊重(甚至可以放在一個列表中創建自己的數據庫虛擬表示)。

這種靈活性和限制(因為不能更改值)也有它的好處,比如傳輸事務數據(或者說類似於表格的格式)。 您“密封”元組中的數據,防止它在發送保護它之前被修改,因為它的設計目的是:提供不變性。 與只讀集合相比,這有什么區別? 您可以有不同的值類型的事實。

它的應用(因為列表,對象和字典的大量使用)受到限制,人們通常認為 object model 將作為更好的選擇(在某些情況下確實如此),但說你不'不想要 object model 因為您更願意將其與您定義為業務實體的內容分開。 然后,一個元組可能會很好地為您提供您想要實現的目標。

列表是可變的,元組是不可變的。 當您嘗試 append 項目時,可變和不可變之間的主要區別是 memory 用法。

創建變量時,會將一些固定的 memory 分配給變量。 如果是列表,則分配的 memory 比實際使用的要多。 例如,如果當前 memory 分配是 100 個字節,當您想要 append 第 101 個字節時,可能會再分配 100 個字節(在這種情況下總共 200 個字節)。

但是,如果您知道自己不經常添加新元素,那么您應該使用元組。 元組精確分配 memory 所需的大小,因此節省了 memory,尤其是當您使用大塊 memory 時。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM