列表与元组,到底用哪个?

在之前的文章《python数据分析(python基础语法)》中介绍过pythond中的六种标准的数据结构,其中列表list,元组tuple长得非常像,你有没有想过,列表和元组到底区别在哪儿呢?本篇文章将回答你的问题。

我们先来回顾下他们的基本概念及方法:

  • 列表list和元组tuple,都是一个可以放置任意数据类型的有序集合,并且都可以随意嵌套。
  • 列表list是动态的,长度大小不固定,可以随意增加、删减或者改变元素。
  • 元组tuple是静态的,长度大小固定,无法增加、删减或改变元素。除非创建一个新的元组。
  • 列表list和元组tuple都支持初始化、索引及切片操作。
  • 列表list和元组tuple都包含一些常用的内置函数:.count().index().reverse()/reversed().sort()/sorted()等。

以上可能都是大家已知的,不再赘述,我们看看列表和元组更深层次的一些区别:

列表与元组存储方式的差异

前面说了,列表和元组最重要的区别就是,列表是动态的、可变的,二元组是静态的、不可变的。这样的差异,势必会影响两者的存储方式。我们来看下面的例子:

l=[1,2,3]
l.__sizeof__()
# 64
tup=(1,2,3)
tup.__sizeof__()
# 48

我们可以看到,对于列表和元组,我们放置了相同的元素,但是元组的存储空间却比列表少16个字节。这是为什么呢?

事实上,由于列表是动态的,所以它需要存储指针,来指向对应的元素(上述例子中,int型为8字节)。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。

l=[]
l.__sizeof__()
# 40  //空列表的存储空间为40字节
l.append(1)
l.__sizeof__()
# 72  //加入元素 1 之后,列表为其分配了可以存储4个元素的空间 (72-40)/8=4
l.append(2)
l.__sizeof__()
# 72  //由于之前已经分配了空间,所以加入元素 2,列表空间不变
l.append(3)
l.__sizeof__()
# 72  //同上
l.append(4)
l.__sizeof__()
# 72  //同上
l.append(5)
l.__sizeof__()
# 104  //加入元素 5 之后,列表的空间不足,所以又额外分配了可以存储4个元素的存储空间

上面的例子,大家对列表list空间分配的过程是不是有了大概的了解?我们可以看到,为了减小每次增加/删减操作时空间分配的开销,python每次分配空间都会额外多分配一些,这样的机制(over-allocating)保证了其操作的高效性。

但是对于元组tuple,情况就不同了。元组长度大小固定,元素不可变,所以存储空间固定。可以看出,元组的要比列表更加轻量级一些。

列表与元组的性能

python有一个垃圾回收机制,就是在后台对静态的数据做一些资源缓存(resourse caching)。如果一些变量不被使用了,python就会回收他们所占用的内存,返还给操作系统,以便其他变量和其他应用使用。

但是对于一些静态变量,比如元组,如果他不被使用,并且占用空间不大时,python会暂时缓存这部分内存。这样,下次我们再创建同样大小的元组时,python就可以不用再向操作系统发出请求,去寻找内存,而是而是可以直接分配之前缓存的内存空间,这样就能大大加快程序运行的速度。

下面的例子,我们用timeit模块的函数来计算初始化一个相同元素的列表和元组,索引操作分别所用的时间。比较下元组和列表的性能:

import timeit

# 初始化
print(timeit.timeit (stmt='x=[1,2,3,4,5]',number=10000000))
# 0.5384310339999274
print(timeit.timeit (stmt='x=(1,2,3,4,5)',number=10000000))
# 0.12699467200002346
# 索引操作
print(timeit.timeit (stmt='x=[1,2,3,4,5];y=x[3]',number=10000000))
# 0.7750472509999327
print(timeit.timeit (stmt='x=(1,2,3,4,5);y=x[3]',number=10000000))
# 0.32637773800001924

可以发现,在执行1000万次循环之后,无论是初始化还是索引操作,元组的耗时都要比元组少,也就是说,元组的性能要优于列表。

列表和元组的使用场景

根据上面的分析,我们可以做一个总结,来区分列表和元组的使用场景:

  • 列表是动态的,长度大小可变,可以随意的增加、删减、改变元素,列表的存储空间略大于元组,且性能稍差。
  • 元组是静态的,长度大小固定,不可以对元素进行增加、删减或更改操作,元组相对于列表更加轻量级,性能更好。
  • 如果存储的数据和数量不变,比如你有一个函数,返回地点的经纬度,那么肯定选用元组更合适。
  • 如果存储的数据或数量可变,比如社交平台上的一个日志功能,是统计一个用户在一周之内看了那些帖子,那么则用列表更合适。

思考题:

想创建一个空列表,我们可以用下面A,B两种方式,请问他们在效率上有事吗区别嘛?我们应该优先考虑是用那种呢?

#创建空列表
#option A
empty_list = list[]

#option B
empty_list = []

大家可以用``timeit`模块来测试下运行速度,在留言里附上你的答案哦!

<think>我们被要求回答Python列表元组的区别,并按照格式给出解释和代码示例,最后提出相关问题。 回答结构: 1. 首先解释列表元组的基本概念和区别。 2. 通过代码示例展示它们的不同之处。 3. 总结关键区别。 4. 最后提出4个相关问题。 列表list)和元组tuple)是Python中两种常用的序列类型,它们都可以存储多个元素,但有一些关键区别: 1. **可变性(Mutability)**: - 列表是可变的(mutable),这意味着创建后可以添加、删除或更改元素。 - 元组是不可变的(immutable),一旦创建,就不能修改(不能添加、删除或更改元素)。 2. **语法**: - 列表使用方括号`[]`定义,例如:`[1, 2, 3]`。 - 元组使用圆括号`()`定义,例如:`(1, 2, 3)`。注意:单个元素的元组需要加逗号,如`(1,)`,否则会被视为普通括号。 3. **性能**: - 由于元组不可变,它们在创建和访问时比列表更快,因为Python元组有优化。 - 元组可以作为字典的键(因为不可变类型才能哈希),而列表不能。 4. **使用场景**: - 列表用于需要动态改变数据的情况。 - 元组用于不改变的数据(如常量集合),或者作为字典的键。 下面通过代码示例来展示这些区别: ```python # 列表的示例 - 可变 my_list = [1, 2, 3] print("Original list:", my_list) # 输出: [1, 2, 3] # 修改列表 my_list[0] = 10 print("Modified list:", my_list) # 输出: [10, 2, 3] # 添加元素 my_list.append(4) print("After append:", my_list) # 输出: [10, 2, 3, 4] # 删除元素 my_list.pop(1) # 删除索引为1的元素 print("After pop:", my_list) # 输出: [10, 3, 4] # 元组的示例 - 不可变 my_tuple = (1, 2, 3) print("\nOriginal tuple:", my_tuple) # 输出: (1, 2, 3) # 尝试修改元组(会引发错误) try: my_tuple[0] = 10 # 这行会抛出TypeError except TypeError as e: print("Error:", e) # 输出: 'tuple' object does not support item assignment # 元组可以作为字典的键 key = ('a', 'b') dictionary = {key: 'value'} print("Dictionary with tuple key:", dictionary) # 输出: {('a', 'b'): 'value'} # 尝试用列表作为字典的键(会失败) try: bad_key = ['a', 'b'] dictionary = {bad_key: 'value'} # 这行会抛出TypeError except TypeError as e: print("Error:", e) # 输出: unhashable type: 'list' ``` 总结: - 列表:可变,使用`[]`,适用于需要修改数据的场景。 - 元组:不可变,使用`()`,适用于不需要修改数据的场景,且可用作字典的键。 §§相关问题§§: 1. 在Python中,如何将列表转换为元组? 2. 在Python中,如何将元组转换为列表? 3. 为什么元组列表的访问速度更快? 4. 在什么情况下该使用元组而不是列表?</think>### Python列表元组的区别 列表list)和元组tuple)都是Python中常用的序列类型,但它们有本质区别。以下是主要差异: #### 1. **可变性(Mutability)** - **列表**:可变(Mutable),创建后可修改内容 - **元组**:不可变(Immutable),创建后内容不可修改 **代码示例**: ```python # 列表可变性示例 my_list = [1, 2, 3] my_list[0] = 10 # 允许修改 my_list.append(4) # 允许添加 print(my_list) # 输出: [10, 2, 3, 4] # 元组不可变性示例 my_tuple = (1, 2, 3) try: my_tuple[0] = 10 # 尝试修改 except TypeError as e: print(f"错误: {e}") # 输出: 'tuple' object does not support item assignment ``` #### 2. **语法差异** - 列表使用方括号 `[]` - 元组使用圆括号 `()`(或直接逗号分隔) **代码示例**: ```python list_example = [1, "a", True] # 列表语法 tuple_example = (1, "b", False) # 元组语法 single_element_tuple = (42,) # 单元素元组必须有逗号 ``` #### 3. **性能比较** - 元组创建和访问速度更快(轻量级) - 列表在修改操作时效率更高 **性能测试**: ```python import timeit # 创建速度测试 list_time = timeit.timeit('l = [1, 2, 3, 4, 5]', number=1000000) tuple_time = timeit.timeit('t = (1, 2, 3, 4, 5)', number=1000000) print(f"列表创建时间: {list_time:.5f}秒") # 约0.03秒 print(f"元组创建时间: {tuple_time:.5f}秒") # 约0.01秒 ``` #### 4. **使用场景** | 场景 | 列表 | 元组 | |------|------|------| | 数据修改 | ✓ 适合 | ✗ 不适合 | | 字典键值 | ✗ 不能作为键 | ✓ 可以作为键 | | 函数返回值 | 通常单值 | 适合返回多个值 | | 数据保护 | ✗ 内容可修改 | ✓ 内容不可变 | **代码示例**: ```python # 元组作为字典键 coordinates = {(35.68, 139.76): "Tokyo", (40.71, -74.01): "New York"} # 函数返回多个值(实际返回元组) def get_dimensions(): return 1920, 1080 # 返回元组(1920, 1080) width, height = get_dimensions() ``` #### 5. **内存占用** - 元组列表占用更少内存 - 差异在大型集合中更明显 **内存测试**: ```python import sys data_list = [1, 2, 3, 4, 5] data_tuple = (1, 2, 3, 4, 5) print(f"列表内存: {sys.getsizeof(data_list)} 字节") # 约96字节 print(f"元组内存: {sys.getsizeof(data_tuple)} 字节") # 约80字节 ``` #### 总结表格 | 特性 | 列表list) | 元组tuple) | |------|--------------|---------------| | 可变性 | 可变 | 不可变 | | 语法 | [元素1, 元素2] | (元素1, 元素2) | | 性能 | 修改高效 | 创建/访问更快 | | 内存占用 | 较高 | 较低 | | 字典键 | 不可用 | 可用 | | 使用场景 | 动态数据集合 | 固定数据序列 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值