列表和元组有啥区别?
Posted
技术标签:
【中文标题】列表和元组有啥区别?【英文标题】:What's the difference between lists and tuples?列表和元组有什么区别? 【发布时间】:2010-10-12 05:21:19 【问题描述】:有什么区别?
元组/列表的优点/缺点是什么?
【问题讨论】:
其他人在下面回答,但我想指出,恕我直言,python 有一个完全不直观的数据类型名称。我认为任何其他语言都没有元组(按那个名字),更糟糕的是,我什至无法用我的语言翻译它。有谁知道“元组”来自哪里?荷兰语? 元组是数学中的一个基本术语,源自拉丁语(参见***)。 pair -> triple -> quadruple -> quintuple -> sextuple -> 嗯,它叫什么,啊,草皮,7-tuple -> 8-tuple -> ...因此是'tuple'作为通用名称。 @JohnFouhy 六年多过去了,但是:...七元组、八元组、九元组元组、十元组、十一元组、十二元组...:D @MegaWidget 我认为我们已经确定非元组是list
。 ;D
【参考方案1】:
与这里制作的许多其他 cmets 一样,我在使用元组时看到的好处是它们能够具有与列表不同类型的值的灵活性。
以一个数据库表为例,它为每一列指定了不同的值和类型。列表根本无法复制这一点(因为它限制为它可以包含的单一类型的值),而元组可以有多种不同的类型和值,并且它们的位置在每列中都得到尊重(甚至可以放在列表中创建您自己的数据库虚拟表示)。
这种灵活性和限制(因为值不能改变)也有它的好处,比如交易数据的传输(或者说类似于表格的格式)。您“密封”元组中的数据,防止它在发送保护它之前被修改,因为它的设计目的是:提供不变性。与只读集合相比,这有什么区别?事实上你可以有不同的值类型。
它的应用(因为列表、对象和字典的大量使用)受到限制,人们普遍认为对象模型将作为更好的选择(在某些情况下确实如此),但是假设您不需要对象模型,因为您更愿意将其与您定义为业务实体的内容分开。那么元组可能会很好地帮助您实现您想要实现的目标。
【讨论】:
【参考方案2】:列表是可变的。而元组是不可变的。在元组中访问带索引的偏移元素比在列表中更有意义,因为元素及其索引无法更改。
【讨论】:
【参考方案3】:最重要的区别是时间!当您不想更改列表内的数据时,最好使用元组!这是为什么使用元组的示例!
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 万次
输出:
0.136621
0.013722200000000018
任何人都可以清楚地注意到时差。
【讨论】:
【参考方案4】:只是对list
的快速扩展vs tuple
回复:
由于动态特性,list
分配的位桶多于实际所需的内存。这样做是为了防止在将来添加额外项目时进行代价高昂的重新分配操作。
另一方面,作为静态的轻量级tuple
对象不会保留存储它们所需的额外内存。
【讨论】:
这些比特桶只是为未来的项目提供额外的“空间”吗?又是如何确定桶的大小和数量的?【参考方案5】:列表和元组的区别
文字
someTuple = (1,2)
someList = [1,2]
尺寸
a = tuple(range(1000))
b = list(range(1000))
a.__sizeof__() # 8024
b.__sizeof__() # 9088
由于元组操作的尺寸较小,它会变得更快一些,但在您拥有大量元素之前,无需过多提及。
允许的操作
b = [1,2]
b[0] = 3 # [3, 2]
a = (1,2)
a[0] = 3 # Error
这也意味着您不能删除元素或对元组进行排序。 但是,您可以向列表和元组添加一个新元素,唯一的区别是,由于元组是不可变的,因此您不是真正添加一个元素,而是创建一个新元组,因此会变的
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
用法
由于列表是可变的,它不能用作字典中的键,而可以使用元组。
a = (1,2)
b = [1,2]
c = a: 1 # OK
c = b: 1 # Error
【讨论】:
那么当我尝试将列表大小调整为较大的值时会发生什么?它会改变内存地址(我认为应该改变id)。或者它会给我一个错误? @WanderingMind:存储列表值的内存地址与存储列表对象本身的内存地址不同。 嗯……这篇文章中的所有代码除了3. Permitted operation
下的第一个框首先显示了元组的情况。我知道通常会先显示成功然后显示错误,但这让我有一段时间感到困惑。
如第3点所示,单个元素列表可以是one_item_list = [a]
,但one_tuple = (a,)
是对应的元组。注意变量名后面的逗号。但也要注意two_tuple = (a, b)
。这不止一次让我失望(在 Python 3 中仍然存在)。
@Cheng 因为对元组进行排序会使它发生变异,即改变它的项目。元组不支持这一点。在python中获取排序元组的最简单方法是tuple(sorted(the_unsorted_tuple))
【参考方案6】:
正如人们已经提到的差异,我将写下为什么使用元组。
为什么首选元组?
小元组的分配优化
为了减少内存碎片并加快分配速度,Python 重用了旧的元组。如果一个 元组不再需要并且少于 20 个项目而不是删除 它会永久地被 Python 移动到一个空闲列表中。
一个空闲列表分为20组,每组代表一个 长度为 n 在 0 到 20 之间的元组列表。每个组可以存储 到 2 000 个元组。第一个(零)组仅包含 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://***.com/a/22140115
【讨论】:
【参考方案7】:正如人们已经在这里回答的那样,tuples
是不可变的,而 lists
是可变的,但是我们必须记住使用元组的一个重要方面
如果tuple
内部包含list
或dictionary
,即使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)
发生这种情况是因为list
和dictionary
是对象,这些对象并没有改变,而是它指向的内容。
所以tuple
毫无例外地保持不变
【讨论】:
如果您解释说“即使元组本身是不可变的,这些也可以更改。” 是因为这些对象保留了它们的身份,而(所以元组没有改变,因为它仍然包含相同的对象...)。【参考方案8】:列表和元组的区别
元组和列表在 Python 中都是看似相似的序列类型。
文字语法
我们使用括号 () 来构造元组和方括号
[ ]
来获得一个新列表。此外,我们可以使用适当类型的调用来获取所需的结构 — 元组或列表。
someTuple = (4,6)
someList = [2,6]
可变性
元组是不可变的,而列表是可变的。这一点是以下几点的基础。
内存使用情况
由于可变性,列表需要更多内存,元组需要更少内存。
扩展
您可以向元组和列表添加新元素,唯一的区别是元组的 id 将被更改(即,我们将拥有一个新对象)。
散列
元组是可散列的,而列表则不是。这意味着您可以将元组用作字典中的键。列表不能用作字典中的键,而可以使用元组
tup = (1,2)
list_ = [1,2]
c = tup : 1 # ok
c = list_ : 1 # error
语义
这一点更多地是关于最佳实践。您应该将元组用作异构数据结构,而列表是同质序列。
【讨论】:
【参考方案9】:如果您去散步,您可以随时在 (x,y)
元组中记下您的坐标。
如果您想记录您的旅程,您可以每隔几秒将您的位置附加到一个列表中。
但你不能反过来做。
【讨论】:
这个例子看起来只是一个约定。人们可能会争辩说“如果我愿意,我仍然可以使用 [x, y] 来记录坐标”。因此,这个答案被认为是未完成的,除非再多一句:“阅读@nikow 的帖子,了解为什么不应该使用列表来存储坐标” @Iceberg,我的回答旨在帮助培养直觉。这并不意味着探索该主题的每一个细微差别。 很好的例子+1。它强调元组元素(这里是坐标)的互补性,这就是不允许修改其中任何一个的原因——因为它改变了整个元组的含义(这里是一个点的位置)。跨度> 【参考方案10】:除了元组是不可变的之外,还有一个语义区别应该指导它们的使用。元组是异构数据结构(即它们的条目具有不同的含义),而列表是同构序列。 元组有结构,列表有顺序。
使用这种区别使代码更加明确和易于理解。
一个例子是成对的页码和行号来引用书中的位置,例如:
my_location = (42, 11) # page number, line number
然后,您可以将其用作字典中的键来存储有关位置的注释。另一方面,列表可用于存储多个位置。自然地,人们可能想要在列表中添加或删除位置,因此列表是可变的是有道理的。另一方面,从现有位置添加或删除项目没有意义 - 因此元组是不可变的。
在某些情况下,您可能想要更改现有位置元组中的项目,例如在遍历页面的行时。但是元组不变性迫使您为每个新值创建一个新的位置元组。从表面上看,这似乎很不方便,但使用像这样的不可变数据是值类型和函数式编程技术的基石,具有很大的优势。
关于这个问题有一些有趣的文章,例如"Python Tuples are Not Just Constant Lists" 或 "Understanding tuples vs. lists in Python"。 Python官方文档also mentions this
“元组是不可变的,通常包含异构序列......”。
在像 Haskell 这样的静态类型语言中,元组中的值通常具有不同的类型,并且元组的长度必须是固定的。在列表中,值都具有相同的类型,并且长度不固定。所以区别非常明显。
最后在 Python 中出现了namedtuple,这是有道理的,因为元组应该已经有了结构。这强调了元组是类和实例的轻量级替代方案。
【讨论】:
+1,特别是对于您的第二个链接有一个很好的例子。喜欢这句话:“这个元组用作轻量级记录或结构。” “列表是同质序列” - 我是 Python 新手,但列表不是异构的吗?来自docs.python.org/py3k/tutorial/introduction.html:“列表项不必都具有相同的类型。”但也许你是在谈论正式的概念,而不是 Python 承担的。 “元组”的良好语义同义词可能是“记录”。它是按特定顺序排列的相关数据项的集合。事实上,我觉得collections.namedtuple
会更好地称为 collections.record
。例如,交换客户记录中的姓名和地址是没有意义的;事实上,这样做通常是一个错误,元组的不变性会阻止你提交。
@nikow:关于What would you do with such a list?
,当人们使用缺乏幻想作为论据时,我总是发抖。使用混合类型列表效果很好,例如对于一些分层数据结构,其中每个列表由子列表和值元素组成。
说元组是异构的而列表是同质的不是误导吗?例如,一个列表可以混合不同的数据类型,即 l = [1, 2, 'a']。我不明白你在说什么。【参考方案11】:
来自5.3. Tuples and Sequences文档的方向引用:
虽然元组可能看起来类似于列表,但它们通常用于不同的情况和不同的目的。元组是不可变的,通常包含一个异构的序列,这些元素通过解包(见本节后面部分)或索引来访问 (或者在命名元组的情况下甚至通过属性)。列表是可变的,它们的元素通常是同构的,并且可以通过迭代列表来访问。
【讨论】:
【参考方案12】:列表是可变的,元组是不可变的。 看看这个例子。
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.
因此证明以下代码对元组无效,因为我们试图更新元组,这是不允许的。
【讨论】:
【参考方案13】:been mentioned 的区别主要是语义上的:人们期望元组和列表代表不同的信息。但这不仅仅是指导方针;一些库实际上根据它们传递的内容而表现不同。以 NumPy 为例(从another post 复制,我要求提供更多示例):
>>> 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 中,列表和元组是完全不同的东西。
【讨论】:
这不是一个真正有用的答案。区别在于type(a_list) != type(a_tuple)
,因此任何基于type(x)
的库代码分支都会表现不同
好点,我已经编辑了帖子:这实际上只是指出语义指南被硬编码到某些库中。
使用 stdlib/builtins 中的示例可能比使用第三方库中的示例更好。有许多地方可以使用单个值或值的元组,并且列表或其他序列被视为单个值。例如,'%d %d' % [2, 3]
是 TypeError
,因为您试图将列表传递给第一个 %d
,而没有将任何值传递给第二个 %d
。 (不过,也有反例,比如max
...)
这很有趣,我不知道python标准库中有这样的例子。你说的几个地方?
您将从这个答案中获得的价值很大程度上取决于您执行的 Python 编程类型。如果您正在进行科学/统计编程,您可能会发现 numpy 示例非常突出。这也是一个常见的面试问题,所以 numpy 是一个非常有用的例子。【参考方案14】:
首先,它们都是Python中的非标量对象(也称为复合对象)。
元组,元素的有序序列(可以包含任何没有别名问题的对象) 不可变(元组、int、float、str) 使用+
进行连接(当然会创建全新的元组)
索引
切片
单例(3,) # -> (3)
而不是(3) # -> 3
列表(其他语言的数组),有序的值序列
可变
单身[3]
克隆new_array = origin_array[:]
列表理解[x**2 for x in range(1,7)]
给你
[1,4,9,16,25,36]
(不可读)
使用列表也可能导致别名错误(两个不同的路径 指向同一个对象)。
【讨论】:
【参考方案15】:这是 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 列表和元组的相似之处在于它们都是有序的值集合。除了使用括号“[ ... , ... ]”创建列表和使用括号“( ... , ... )”创建元组的细微区别之外,它们之间的核心技术“硬编码 Python 语法”区别是特定元组的元素是不可变的,而列表是可变的(......所以只有元组是可散列的,可以用作字典/散列键!)。这会导致它们如何使用或不能使用的差异(通过语法先验地强制执行)以及人们选择使用它们的方式的差异(被鼓励为“最佳实践”,后验,这就是 smart 程序员做的)。区分何时使用元组与何时使用列表的主要区别在于人们对元素顺序的含义。
对于元组,“顺序”仅表示用于保存信息的特定“结构”。在第一个字段中找到的值可以轻松切换到第二个字段,因为每个字段都提供跨两个不同维度或比例的值。它们为不同类型的问题提供答案,通常具有以下形式:对于给定的对象/主题,它的属性是什么?对象/主题保持不变,属性不同。
对于列表,“顺序”表示顺序或方向性。第二个元素必须在第一个元素之后,因为它根据特定和常见的比例或尺寸位于第二位。元素被视为一个整体,并且主要提供对单个问题的答案,通常是表单,对于给定的属性,这些对象/主题如何比较?属性保持不变,对象/主题不同.
在流行文化中不符合这些差异的人和程序员的例子不计其数,并且有不计其数的人可能会在主菜中使用沙拉叉。归根结底,这很好,两者通常都能完成工作。
总结一些更精细的细节
相似之处:
-
重复 - 元组和列表都允许重复
索引、选择和切片 - 元组和列表都使用括号内的整数值进行索引。因此,如果您想要给定列表或元组的前 3 个值,语法将是相同的:
>>> my_list[0:3]
[0,1,2]
>>> my_tuple[0:3]
[a,b,c]
比较和排序 - 两个元组或两个列表都通过它们的第一个元素进行比较,如果存在平局,则通过第二个元素进行比较,依此类推。前面的元素出现差异后,不再关注后面的元素。
>>> [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
差异: - 先验,根据定义
语法 - 列表使用 [],元组使用 ()
可变性 - 给定列表中的元素是可变的,给定元组中的元素是不可变的。
# 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
哈希表(字典) - 由于哈希表(字典)要求其键是可散列的,因此是不可变的,因此只有元组可以充当字典键,而不是列表。
#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
差异 - 使用中的后验
元素的同质性与异质性 - 通常列表对象是同质的,而元组对象是异质的。也就是说,列表用于相同类型的对象/主题(例如所有总统候选人,或所有歌曲,或所有跑步者),尽管它不是强制的),而元组更多用于异构对象。
李>循环与结构 - 虽然两者都允许循环(对于 my_list 中的 x...),但仅对列表执行此操作才有意义。元组更适合结构化和呈现信息(位于 %s 中的 %s %s 是 %s,目前是 %s % ("John","Wayne",90210, "Actor","Dead"))
【讨论】:
【参考方案16】:PEP 484 -- Type Hints 表示tuple
的元素类型可以单独键入;这样你就可以说Tuple[str, int, float]
;但是一个list
,带有List
类型的类只能接受一个类型参数:List[str]
,这暗示了两者的不同之处在于前者是异构的,而后者本质上是同质的。
此外,标准库大多使用元组作为此类标准函数的返回值,其中 C 将返回 struct
。
【讨论】:
【参考方案17】:主要区别在于元组是不可变的。这意味着一旦创建元组,您就无法更改元组中的值。
因此,如果您需要更改值,请使用列表。
元组的好处:
-
性能略有提升。
由于元组是不可变的,它可以用作字典中的键。
如果您无法更改,其他人也无法更改,也就是说您无需担心任何 API 函数等会在不被询问的情况下更改您的元组。
【讨论】:
请注意,元组只有在其所有元素都是不可变的时才是不可变的。您可以说所有不可变集合都是如此,例如frozenset
或各种第三方冻结的字典/树/等。类型,但这些都不允许您添加可变元素。 (当然,一个元组只有在它的所有元素都是可散列的,它以通常的 EAFP 方式处理,所以d[1, [2]]
将引发TypeError: unhashable type: 'list'
。)
一个元组只有在其所有元素都是不可变的情况下才能用作字典中的键。见here【参考方案18】:
列表是可变的,元组是不可变的。 mutable 和 immutable 之间的主要区别在于尝试追加项目时的内存使用情况。
创建变量时,会为变量分配一些固定内存。如果是列表,则分配的内存比实际使用的多。例如。如果当前的内存分配是 100 字节,当您想附加第 101 个字节时,可能会再分配 100 个字节(在这种情况下总共 200 个字节)。
但是,如果您知道自己不经常添加新元素,那么您应该使用元组。元组精确分配所需内存的大小,因此可以节省内存,尤其是在您使用大块内存时。
【讨论】:
虽然其中一些在技术上是正确的,但这并不是可变类型和不可变类型之间的关键区别。更大的区别是可变类型可以在构造后更改,而不可变类型则不能。 这也不是原因。内存和可变性彼此无关。这只是特定于列表的实现细节。内存也没有分配给变量,而是分配给对象。变量只是对这些对象的引用。【参考方案19】:列表是可变的;元组不是。
来自docs.python.org/2/tutorial/datastructures.html
元组是不可变的,通常包含一个异构的序列 通过解包访问的元素(见本节后面) 或索引(甚至在命名元组的情况下按属性)。列表 是可变的,并且它们的元素通常是同质的并且是 通过遍历列表访问。
【讨论】:
我真的认为您还应该考虑语义含义(请参阅下面的答案)。 现在看来几乎不值得付出努力,但感谢您的提醒。 好的,这是链接 - docs.python.org/2/library/stdtypes.html#mutable-sequence-types ;-) @duffymo 我认为这个答案是这个页面上最清晰、最简洁的答案。它指出了元组和列表之间唯一真正重要的区别,并且不会无休止地喋喋不休地谈论这种公然错误的同质与异构的废话。【参考方案20】:list的值可以随时更改,但tuples的值不能更改。
优点和缺点取决于用途。如果你有这样一个你不想改变的数据,那么你应该使用元组,否则列表是最好的选择。
【讨论】:
【参考方案21】:列表用于循环,元组用于结构,即"%s %s" %tuple
。
列表通常是同质的,元组通常是异构的。
列表是可变长度的,元组是固定长度的。
【讨论】:
【参考方案22】:列表旨在成为同质序列,而元组是异构数据结构。
【讨论】:
在这一点上,这个答案并没有增加任何讨论,因为还有许多其他更好的答案。以上是关于列表和元组有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章