卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

    • Python有哪些内置数据类型?
    • Python变量保存的是什么?
    • 可变对象和不可变对象有什么区别?
    • is和==有什么区别?
    • Python的运算符有哪些?
    • Python条件判断和循环的常见写法与易错点
    • Python字符串为什么不可变?
    • Python列表和元组有什么区别?
      • 简要回答
      • 详细回答
      • 知识拓展
    • Python字典和集合有什么区别?
    • Python切片和序列解包怎么用?
    • 列表、字典和集合推导式怎么写?
  • 函数、作用域与模块

  • 面向对象

  • 进阶语法与语言机制

# Python列表和元组有什么区别?

面试官问:“Python中的列表和元组有什么区别?”

很多录友答完“列表可变、元组不可变”就停了。面试官接着问一句“那元组一定能当字典键吗”“元组里放列表,列表能改吗”,答案就开始含糊。这道题的关键是讲清不可变到底约束了什么,以及它怎样影响哈希、内存和使用场景。可变与不可变的通用规则见《Python中的可变对象和不可变对象有什么区别?》。

# 简要回答

列表list和元组tuple都是有序序列,都支持索引、切片、遍历和in判断。核心区别是:列表可变,可以原地增删改元素;元组不可变,创建后不能增删元素,也不能把某个位置换成别的对象。

这个区别带来几个直接结果。元组的元素都可哈希时,元组本身才可哈希,可以作为字典键或集合元素;列表永远不可哈希。在CPython中,元组没有为扩容预留空间,同样元素数量下通常比列表占用更少内存。

语义上,列表适合放数量会变化的同类数据,例如用户列表;元组适合放结构固定的一组值,例如坐标、数据库的一行记录、函数返回的多个值。

# 详细回答

# 原地修改与创建新对象

列表的append、索引赋值、sort都会原地修改同一个对象;元组没有这些修改方法,想“改”只能创建新元组:

courses = ["Python", "Java"]
alias = courses
courses.append("Go")
print(alias)              # ['Python', 'Java', 'Go'],alias看到同一个列表的变化

point = (3, 5)
# point[0] = 10           # TypeError: 'tuple' object does not support item assignment
moved = (10,) + point[1:]
print(point, moved)       # (3, 5) (10, 5)
1
2
3
4
5
6
7
8
9

alias和courses指向同一个列表,append修改的是这个列表本身,所以两个名字都能看到"Go"。元组point没有被修改,moved是拼接出来的新元组。

# 元组不可变,不等于里面的对象不可变

元组只保证每个位置保存的引用不变,不管被引用的对象自己能不能变:

record = ("Alice", [90, 85])
record[1].append(100)
print(record)             # ('Alice', [90, 85, 100])

# record[1] = [60]        # TypeError,不能把这个位置换成别的对象
print(hash(("Alice", 90)))     # 正常输出一个整数
# hash(record)            # TypeError: unhashable type: 'list'
1
2
3
4
5
6
7

record[1]仍然是原来那个列表,只是列表内容变了,元组的约束没有被打破。也正因为里面有可变的列表,record的内容可能随时变化,所以它不能计算哈希,不能当字典键。

元组引用与内部可变对象

图里要区分两层:元组这一层的“槽位引用”被锁死,替换会报错;槽位指向的列表那一层仍然可变,append可以成功。哈希要求整个值都稳定,只要有一层能变,元组就不能哈希。

# 内存占用与性能

在CPython中,列表为了让append的均摊时间复杂度保持O(1),扩容时会多申请一些空间;元组长度固定,不需要预留。以64位的CPython 3.13为例:

import sys

print(sys.getsizeof([1, 2, 3, 4, 5]))   # 104
print(sys.getsizeof((1, 2, 3, 4, 5)))   # 80
1
2
3
4

sys.getsizeof只统计容器本身,不包含元素对象,具体数值随版本和平台变化。可以确认的趋势是:同样元素数量下,元组通常更省内存;CPython还会把全部由常量组成的元组字面量直接放进代码对象,创建时开销更小。

但不要把“元组更快”当成换掉列表的理由。两者按下标访问都是O(1),真实程序的耗时通常不在这里。先按语义选类型,再考虑这点性能差异。

# 怎么选

对比项 列表 list 元组 tuple
可变性 可变,支持增删改 不可变,不能替换元素
可哈希 不可哈希 元素都可哈希时可哈希
内存(CPython) 有扩容预留 长度固定,通常更省
典型语义 数量会变的同类数据 结构固定的一组值
常见场景 待办任务、查询结果集合 坐标、字典的复合键、多返回值

一个实用的判断方法:如果每个位置都有固定含义(第0位是姓名,第1位是年龄),用元组;如果每个元素地位相同、数量会变,用列表。字典键要用复合值时,例如(城市, 日期),只能用元组,字典对键的要求见《Python字典和集合有什么区别?》。

# 知识拓展

面试官可能追问:

Q1:函数return a, b返回的是什么?

返回的是一个元组,括号可以省略,真正构成元组的是逗号。调用方写name, age = get_user()时,是把元组解包给两个名字。解包和星号收集的更多写法见《Python切片和序列解包怎么用?》。

Q2:只有一个元素的元组怎么写?

要写成(42,)。(42)里的括号只是分组,结果还是整数42。空元组写作()或tuple()。

Q3:tuple(some_list)会把里面的列表也变成不可变吗?

不会。转换只创建一个新的元组容器,元素引用和原列表里的完全相同。原列表中如果有嵌套列表,转换后它依旧是同一个可变列表,修改会同时体现在原列表和新元组里。

Q4:元组+=为什么不报错?

t += (3,)对元组来说是创建一个新元组,再让t重新绑定过去,原元组没变。如果另一个名字也指向原元组,它看到的还是旧内容。列表+=则是原地扩展,和extend效果一样,所有指向该列表的名字都会看到变化。

Last Updated: 10/13/2026, 5:57:00 PM

← Python字符串为什么不可变? Python字典和集合有什么区别? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪