Skip to content

Python list 和 tuple:从"购物车"到"保险箱"的思维跃迁

引言:为什么需要"容器"?

想象你是一家餐厅的店长,每天要记录顾客的订单。你可以用便签纸一张张写,但这样查找、修改、统计都非常麻烦。更好的办法是准备一个订单本——按顺序记录,随时翻看,随时修改。

在编程世界里,list 和 tuple 就是这样的"容器"。它们用来按顺序存放多个数据,是 Python 中最基础、最常用的数据结构。

但这两个容器性格迥异:list 像一本活页笔记本,可以随时增删改查;tuple 像一本装订好的书,一旦写完就不能再改。理解它们的区别,是写出安全、高效代码的关键。


一、list:你的"活页笔记本"

1.1 什么是 list?

list(列表)是 Python 内置的有序、可变的数据集合。用方括号 [] 表示,元素之间用逗号分隔。

python
# 记录班里同学的名字
classmates = ['Michael', 'Bob', 'Tracy']

# 记录今天的待办事项
todo_list = ['写代码', '开会', '健身', '看书']

# 记录商品价格
prices = [9.9, 19.8, 29.9, 99.0]

生活化理解:list 就像你手机里的购物车——商品按加入顺序排列,可以随时添加新商品、删除不想要的、修改数量。

1.2 创建 list 的几种方式

python
# 方式1:直接列出元素
fruits = ['苹果', '香蕉', '橙子']

# 方式2:空 list,后续添加
shopping_cart = []

# 方式3:用 list() 转换其他数据
numbers = list(range(5))        # [0, 1, 2, 3, 4]
chars = list('hello')           # ['h', 'e', 'l', 'l', 'o']

1.3 访问元素:索引的艺术

list 中的每个元素都有一个位置编号,叫做索引(index)

python
classmates = ['Michael', 'Bob', 'Tracy']
#              0          1       2     ← 正索引
#             -3         -2      -1     ← 负索引

print(classmates[0])    # Michael(第1个)
print(classmates[1])    # Bob(第2个)
print(classmates[-1])   # Tracy(倒数第1个)
print(classmates[-2])   # Bob(倒数第2个)

关键规则

  • 正索引从 0 开始(不是 1!)
  • 负索引从 -1 开始(倒数第一个)
  • 最后一个元素的正索引 = len(list) - 1

为什么从 0 开始? 这是计算机科学的传统。想象楼层:地面层是 0 楼,往上是 1、2、3...,往下是 -1、-2...

1.4 越界:常见的 IndexError

python
classmates = ['Michael', 'Bob', 'Tracy']
print(classmates[3])    # 报错!只有 0,1,2 三个索引
# IndexError: list index out of range

生活化理解:你的购物车只有 3 件商品,你却要看第 4 件——系统只能报错。

防御性写法

python
if len(classmates) > 3:
    print(classmates[3])
else:
    print('没有第4个元素')

1.5 len():数一数有多少个

python
classmates = ['Michael', 'Bob', 'Tracy']
print(len(classmates))    # 3

empty_list = []
print(len(empty_list))    # 0

len() 是 Python 的通用函数,字符串、list、tuple、字典等都可以用。

1.6 添加元素:append() 和 insert()

append():在末尾追加

python
classmates = ['Michael', 'Bob', 'Tracy']
classmates.append('Adam')
print(classmates)    # ['Michael', 'Bob', 'Tracy', 'Adam']

insert():在指定位置插入

python
classmates = ['Michael', 'Bob', 'Tracy']
classmates.insert(1, 'Jack')    # 在索引1的位置插入
print(classmates)    # ['Michael', 'Jack', 'Bob', 'Tracy']

生活化理解

  • append() 像排队时排在最后
  • insert() 像插队——后面的人依次后移

1.7 删除元素:pop() 和 remove()

pop():删除并返回指定位置的元素

python
classmates = ['Michael', 'Bob', 'Tracy']

# 删除末尾元素
last = classmates.pop()
print(last)         # Tracy
print(classmates)   # ['Michael', 'Bob']

# 删除指定位置
second = classmates.pop(1)
print(second)       # Bob
print(classmates)   # ['Michael']

remove():删除指定值的元素

python
fruits = ['苹果', '香蕉', '橙子', '香蕉']
fruits.remove('香蕉')    # 只删除第一个匹配的
print(fruits)           # ['苹果', '橙子', '香蕉']

del 语句:按索引删除

python
classmates = ['Michael', 'Bob', 'Tracy']
del classmates[1]
print(classmates)    # ['Michael', 'Tracy']

1.8 修改元素:直接赋值

python
classmates = ['Michael', 'Bob', 'Tracy']
classmates[1] = 'Sarah'
print(classmates)    # ['Michael', 'Sarah', 'Tracy']

1.9 list 的"混搭"与嵌套

元素类型可以不同

python
mixed = ['Apple', 123, True, 3.14, None]

list 可以嵌套 list

python
s = ['python', 'java', ['asp', 'php'], 'scheme']
print(len(s))       # 4(不是5!)
print(s[2])         # ['asp', 'php']
print(s[2][1])      # 'php'

生活化理解:就像书架上的一个格子,里面可以放一本书(普通元素),也可以放一个小盒子,盒子里再装几本书(嵌套 list)。

二维表格的表示

python
# 3行3列的成绩表
scores = [
    [85, 92, 78],    # 第1个学生的3科成绩
    [90, 88, 95],    # 第2个学生
    [76, 85, 82]     # 第3个学生
]
print(scores[0][1])    # 92(第1个学生的第2科)

1.10 list 的常用操作速查

操作方法示例结果
添加append(x)l.append(4)末尾添加
插入insert(i, x)l.insert(0, 'a')索引0处插入
删除末尾pop()l.pop()删除并返回最后一个
删除指定pop(i)l.pop(0)删除并返回索引0的元素
按值删除remove(x)l.remove('a')删除第一个匹配项
修改l[i] = xl[0] = 'b'修改索引0的值
查找索引index(x)l.index('a')返回首次出现的索引
统计次数count(x)l.count('a')统计出现次数
排序sort()l.sort()原地排序
反转reverse()l.reverse()原地反转
切片l[1:3]l[1:3]获取子列表

二、tuple:你的"装订好的书"

2.1 什么是 tuple?

tuple(元组)是有序、不可变的数据集合。用圆括号 () 表示。

python
# 定义一个 tuple
classmates = ('Michael', 'Bob', 'Tracy')

# 坐标点
point = (3, 5)

# 数据库记录
user = ('张三', 25, '北京', '工程师')

生活化理解:tuple 就像打印出来的纸质书——内容固定,不能涂改。你可以阅读(访问元素),但不能撕掉一页或插入一页。

2.2 tuple 的访问方式

与 list 完全相同:

python
classmates = ('Michael', 'Bob', 'Tracy')

print(classmates[0])    # Michael
print(classmates[-1])   # Tracy
print(len(classmates))  # 3

2.3 为什么需要 tuple?

你可能会问:list 已经很好用了,为什么还要 tuple?

原因 1:安全性

python
# 用 tuple 存储不应该被修改的数据
birth_date = (1990, 5, 15)    # 出生日期,永远不变

# 如果用 list,不小心就会被修改
birth_date_list = [1990, 5, 15]
birth_date_list[0] = 2000     # 被篡改了!

原因 2:性能 tuple 比 list 更轻量,创建和访问速度更快。

原因 3:字典的 key

python
# tuple 可以作为字典的 key,list 不行
location_map = {
    (39.9, 116.4): '北京',
    (31.2, 121.5): '上海'
}

2.4 tuple 的"陷阱":单元素 tuple

python
# ❌ 错误:这不是 tuple,是数字 1
t = (1)
print(type(t))    # <class 'int'>

# ✅ 正确:单元素 tuple 必须加逗号
t = (1,)
print(type(t))    # <class 'tuple'>

为什么? 因为 () 在数学运算中也有意义,比如 (1 + 2) * 3。Python 无法区分 (1) 是"括号里的 1"还是"单元素 tuple",所以规定:单元素 tuple 必须加逗号

2.5 "可变的" tuple:指向不变

这是最容易迷惑的地方:

python
t = ('a', 'b', ['A', 'B'])
print(t)    # ('a', 'b', ['A', 'B'])

# 修改 tuple 里的 list
t[2][0] = 'X'
t[2][1] = 'Y'
print(t)    # ('a', 'b', ['X', 'Y'])

不是 tuple 不能变吗?怎么变了?

生活化理解

  • tuple 像是一个相框,里面放了 3 张照片:'a'、'b' 和一个 list
  • 相框本身不能换照片(tuple 元素不能重新赋值)
  • 但第 3 张照片是一个相册(list),相册里的照片可以换

"不变"的真正含义:tuple 的每个元素指向的对象不能变,但对象本身的内容可以变。

python
# 画图理解
# t[0] → 'a'(不能改成指向 'c')
# t[1] → 'b'(不能改成指向 'd')
# t[2] → ['A', 'B'](不能改成指向其他 list,但这个 list 内容可以改)

2.6 tuple 的常用操作

python
t = (1, 2, 3, 2, 2)

# 访问
print(t[0])        # 1
print(t[-1])       # 2

# 统计
print(t.count(2))  # 3
print(t.index(3))  # 2(首次出现的索引)

# 连接
t1 = (1, 2)
t2 = (3, 4)
print(t1 + t2)     # (1, 2, 3, 4)

# 重复
print(t1 * 3)      # (1, 2, 1, 2, 1, 2)

三、list vs tuple:如何选择?

对比维度listtuple
可变性可变(增删改)不可变
语法[]()
性能较慢较快
内存占用较大较小
字典 key不能
适用场景数据会变化数据固定不变

选择原则

  • 数据需要修改?→ 用 list
  • 数据固定不变?→ 用 tuple
  • 要作为字典 key?→ 必须用 tuple
  • 不确定?→ 优先 tuple(更安全)

实际应用场景

python
# 用 list:购物车(会增删商品)
shopping_cart = ['苹果', '香蕉']

# 用 tuple:地理坐标(固定不变)
beijing = (39.9, 116.4)

# 用 list:待办事项(会增删改)
todo_list = ['写报告', '开会']

# 用 tuple:函数返回多个值
def get_user_info():
    return ('张三', 25, '北京')    # 返回 tuple

name, age, city = get_user_info()  # 解包

四、常见误区与陷阱

4.1 误区一:索引从 1 开始

python
# 很多初学者以为
classmates = ['Michael', 'Bob', 'Tracy']
print(classmates[1])    # 以为是 Michael,实际是 Bob

# 正确:第 1 个元素的索引是 0
print(classmates[0])    # Michael

4.2 误区二:append() 的返回值

python
# ❌ 错误:append() 返回 None,不是新 list
l = [1, 2, 3]
new_l = l.append(4)
print(new_l)    # None
print(l)        # [1, 2, 3, 4]

# ✅ 正确:append() 是原地修改
l = [1, 2, 3]
l.append(4)
print(l)        # [1, 2, 3, 4]

4.3 误区三:单元素 tuple 忘记逗号

python
# 错误
t = (1)         # 这是数字 1,不是 tuple

# 正确
t = (1,)        # 这才是 tuple

4.4 误区四:tuple 完全不能变

python
# 错误理解:tuple 里的所有内容都不能变
t = (1, 2, [3, 4])
t[2].append(5)    # 这实际上是可以的!
print(t)          # (1, 2, [3, 4, 5])

# 正确理解:tuple 的"指向"不能变,但指向的对象内容可以变
# t[2] 不能指向另一个 list,但这个 list 本身可以修改

4.5 误区五:复制 list 的陷阱

python
# ❌ 错误:这是引用,不是复制
l1 = [1, 2, 3]
l2 = l1
l2.append(4)
print(l1)    # [1, 2, 3, 4](l1 也被改了!)

# ✅ 正确:复制 list
l1 = [1, 2, 3]

# 方法1:切片
l2 = l1[:]

# 方法2:list()
l2 = list(l1)

# 方法3:copy 模块(浅拷贝)
import copy
l2 = copy.copy(l1)

# 深拷贝(嵌套 list 时)
l3 = copy.deepcopy(l1)

五、实战案例

案例 1:学生成绩管理

python
# 用嵌套 list 存储学生成绩
students = [
    ['张三', [85, 92, 78]],
    ['李四', [90, 88, 95]],
    ['王五', [76, 85, 82]]
]

# 打印所有学生的平均分
for name, scores in students:
    avg = sum(scores) / len(scores)
    print(f'{name} 的平均分:{avg:.1f}')

# 输出:
# 张三 的平均分:85.0
# 李四 的平均分:91.0
# 王五 的平均分:81.0

案例 2:简单的队列系统

python
# 用 list 模拟排队
queue = []

# 入队
queue.append('顾客A')
queue.append('顾客B')
queue.append('顾客C')
print(f'当前队列:{queue}')    # ['顾客A', '顾客B', '顾客C']

# 出队(先进先出)
first = queue.pop(0)
print(f'服务:{first}')        # 顾客A
print(f'剩余队列:{queue}')    # ['顾客B', '顾客C']

案例 3:坐标系统

python
# 用 tuple 存储坐标点
points = [(0, 0), (1, 1), (2, 4), (3, 9)]

# 计算每个点到原点的距离
import math
for x, y in points:
    distance = math.sqrt(x**2 + y**2)
    print(f'点({x}, {y}) 到原点的距离:{distance:.2f}')

六、动手练习

练习 1:用索引取出下面 list 的指定元素

python
L = [
    ['Apple', 'Google', 'Microsoft'],
    ['Java', 'Python', 'Ruby', 'PHP'],
    ['Adam', 'Bart', 'Bob']
]

# 打印 Apple
print(L[0][0])

# 打印 Python
print(L[1][1])

# 打印 Bob
print(L[2][2])

练习 2:判断哪些是 tuple

python
a = ()          # tuple(空 tuple)
b = (1)         # int(不是 tuple!)
c = [2]         # list
d = (3,)        # tuple(注意逗号)
e = (4,5,6)     # tuple

总结

概念核心要点
list有序、可变、用 []、索引从 0 开始
tuple有序、不可变、用 ()、单元素要加逗号
索引正索引 0 开始,负索引 -1 开始
append()原地修改,返回 None
tuple 的"不变"指向不变,但指向的对象内容可以变
选择原则数据可变选 list,固定选 tuple

记住:list 是你的活页笔记本,tuple 是你的保险箱。根据数据是否需要修改来选择,不确定时优先 tuple(更安全)


本文基于廖雪峰 Python 教程重新撰写,补充了大量背景知识、生活化类比和实战场景。推荐继续阅读:条件判断