Python 列表(List)与元组(Tuple)详解

Python 列表(List)与元组(Tuple)详解

核心区别一句话总结:列表可变,元组不可变。这一差异决定了它们各自的应用场景和性能特性。

在 Python 中,列表(List)和元组(Tuple)是两种常用的序列类型,用于存储多个元素。它们的核心区别在于可变性:列表是可变的(元素可修改),而元组是不可变的(元素一旦创建就无法修改)。本文将详细介绍两者的用法、区别及适用场景。

列表(List):可变的序列

列表是 Python 中最灵活的序列类型,使用方括号 [] 定义,元素之间用逗号分隔,支持添加、删除、修改等操作。

基本定义与创建
python 复制代码
# 空列表
empty_list = []
# 包含不同类型元素的列表(Python 列表支持异构元素)
mixed_list = [1, "apple", 3.14, True]
# 嵌套列表(列表中包含列表)
nested_list = [1, [2, 3], [4, [5, 6]]]
访问元素

通过索引 (下标)访问元素,索引从 0 开始,支持负数索引(从末尾计数,-1 表示最后一个元素)。

python 复制代码
fruits = ["apple", "banana", "cherry", "date"]

# 访问单个元素
print(fruits[0])   # 输出:apple(第一个元素)
print(fruits[-1])  # 输出:date(最后一个元素)

# 分片(切片):获取子列表,语法为 [start:end:step],左闭右开
print(fruits[1:3])   # 输出:['banana', 'cherry'](索引1到2的元素)
print(fruits[:2])    # 输出:['apple', 'banana'](从开头到索引1)
print(fruits[2:])    # 输出:['cherry', 'date'](从索引2到结尾)
print(fruits[::2])   # 输出:['apple', 'cherry'](步长为2,间隔一个元素)

# 复制列表(创建副本,修改副本不影响原列表)
fruits_copy = fruits[:]

# 获取元素对应索引
index = fruits.index(target)
修改元素

列表是可变的,可直接通过索引修改元素:

python 复制代码
numbers = [1, 2, 3, 4]
numbers[1] = 20  # 将索引1的元素改为20
print(numbers)   # 输出:[1, 20, 3, 4]
添加元素
  • append(x):在列表末尾添加元素 x
  • insert(index, x):在指定索引 index 处插入元素 x
  • extend(iterable):将可迭代对象(如列表、元组)的元素添加到末尾。
python 复制代码
colors = ["red", "green"]

colors.append("blue")  # 末尾添加
print(colors)  # 输出:['red', 'green', 'blue']

colors.insert(1, "yellow")  # 索引1处插入
print(colors)  # 输出:['red', 'yellow', 'green', 'blue']

colors.extend(["purple", "orange"])  # 批量添加
print(colors)  # 输出:['red', 'yellow', 'green', 'blue', 'purple', 'orange']
删除元素
  • del list[index]:删除指定索引的元素。
  • list.remove(x):删除第一个值为 x 的元素(若不存在则报错)。
  • list.pop(index):删除并返回指定索引的元素(默认删除最后一个)。
python 复制代码
languages = ["Python", "Java", "C++", "Python"]

del languages[1]  # 删除索引1的元素
print(languages)  # 输出:['Python', 'C++', 'Python']

languages.remove("Python")  # 删除第一个"Python"
print(languages)  # 输出:['C++', 'Python']

popped = languages.pop()  # 删除最后一个元素
print(popped)     # 输出:Python
print(languages)  # 输出:['C++']
常用操作
  • len(list):获取列表长度。
  • list.count(x):统计元素 x 出现的次数。
  • list.sort():对列表排序(原地修改)。
  • list.reverse():反转列表(原地修改)。
python 复制代码
nums = [3, 1, 4, 1, 5]

print(len(nums))       # 输出:5(长度)
print(nums.count(1))   # 输出:2(1出现的次数)

nums.sort()            # 排序
print(nums)            # 输出:[1, 1, 3, 4, 5]

nums.reverse()         # 反转
print(nums)            # 输出:[5, 4, 3, 1, 1]

元组(Tuple):不可变的序列

元组使用小括号 () 定义,元素不可修改,适合存储不需要变更的数据。

基本定义与创建
python 复制代码
# 普通元组
tup1 = (1, 2, 3, 4)
# 单元素元组(必须加逗号,否则会被视为普通括号)
single_tuple = (5,)
# 空元组
empty_tuple = ()
# 省略括号的元组(Python 允许)
implicit_tuple = 10, 20, 30
# 嵌套元组
nested_tuple = (1, (2, 3), (4, 5, 6))
访问元素

元组的访问方式与列表完全相同,支持索引和分片:

python 复制代码
animals = ("cat", "dog", "bird", "fish")

print(animals[2])      # 输出:bird(索引2的元素)
print(animals[-2])     # 输出:bird(倒数第二个元素)
print(animals[1:3])    # 输出:('dog', 'bird')(分片)
不可变性说明

元组的元素一旦创建就无法修改,试图修改会报错:

python 复制代码
tup = (1, 2, 3)
tup[0] = 10  # 报错:TypeError: 'tuple' object does not support item assignment

注意:如果元组中包含可变元素(如列表),则该元素内部可以修改:

python 复制代码
mutable_in_tuple = (1, [2, 3], 4)
mutable_in_tuple[1][0] = 20  # 元组中的列表元素可修改
print(mutable_in_tuple)  # 输出:(1, [20, 3], 4)
元组的常用操作

虽然元组不可变,但支持以下操作:

  • len(tuple):获取长度。
  • tuple.count(x):统计元素 x 出现的次数。
  • tuple.index(x):返回元素 x 第一次出现的索引。
  • 元组拼接(创建新元组,原元组不变)。
python 复制代码
t1 = (1, 2, 3)
t2 = (4, 5)

print(len(t1))          # 输出:3
print(t1.count(2))      # 输出:1
print(t1.index(3))      # 输出:2

t3 = t1 + t2  # 拼接元组(创建新元组)
print(t3)     # 输出:(1, 2, 3, 4, 5)
函数返回多参数

严格来说,一个函数只能返回一个值,但是如果这个值是一个元组,效果就会和返回多个值一样了

复制代码
def get_name_and_age():
    name = "Alice"
    age = 25
    return name, age  # 这会返回一个元组 (('Alice', 25),)

name, age = get_name_and_age()
print(name)
print(age)

列表与元组的核心区别

特性 列表(List) 元组(Tuple)
定义符号 方括号 [] 小括号 ()(可省略)
可变性 可变(可修改、添加、删除元素) 不可变(元素创建后无法修改)
性能 略低(需维护可变结构) 更高(内存占用少,访问速度快)
适用场景 元素需动态修改(如数据收集) 元素固定不变(如配置、常量)
哈希性 不可哈希(不能作为字典的键) 可哈希(可作为字典的键)

如何选择:列表还是元组?

  • 用列表:当需要添加、删除或修改元素时(如动态收集用户输入、存储可变更的数据集)。
  • 用元组:当数据一旦创建就不需要修改时(如存储配置项、函数返回多个值、作为字典的键)。

示例:函数返回多个值(本质是返回元组)

python 复制代码
def get_user_info():
    name = "Alice"
    age = 30
    return name, age  # 隐式返回元组

user = get_user_info()
print(user)  # 输出:('Alice', 30)(元组)
name, age = user  # 解包(元组特有的便捷操作)
print(name, age)  # 输出:Alice 30

查看类型

如果不确定值是什么类型,可以使用type来进行判断

python 复制代码
>>> type(6)
<class 'int'>
>>> type('H')
<class 'str'>
>>> fruits = ["apple", "banana", "cherry", "date"]
>>> type(fruits)
<class 'list'>
>>> tup1 = (1, 2, 3, 4)
>>> type(tup1)
<class 'tuple'>
>>> 

快速选型指南

场景 推荐类型
动态收集用户输入 列表 ✅
存储配置常量(如颜色RGB值) 元组 ✅
作为字典的键 元组 ✅
函数返回多个值 元组 ✅
需要排序、反转、增删操作 列表 ✅
数据量大且只读(提高性能) 元组 ✅
与其他开发者协作(明确数据不可变意图) 元组 ✅

常见陷阱与最佳实践

🚨 陷阱 1:使用可变对象作为默认参数
python 复制代码
# ❌ 错误示例(默认参数是可变对象)
def add_item(item, my_list=[]):
    my_list.append(item)
    return my_list

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2] ← 意外!多次调用共享了同一个列表

# ✅ 正确做法
def add_item(item, my_list=None):
    if my_list is None:
        my_list = []
    my_list.append(item)
    return my_list

print(add_item(1))  # [1]
print(add_item(2))  # [2]
🚨 陷阱 2:浅拷贝 vs 深拷贝
python 复制代码
import copy

nested = [[1, 2], [3, 4]]

# 浅拷贝:只复制外层,内层列表仍共享
shallow = nested[:]
shallow[0][0] = 99
print(nested)   # [[99, 2], [3, 4]] ← 原列表也被修改!

# 深拷贝:完全独立
deep = copy.deepcopy(nested)
deep[0][0] = 999
print(nested)   # [[99, 2], [3, 4]] ← 原列表不受影响
🚨 陷阱 3:列表乘法与引用
python 复制代码
# ❌ 错误:创建了多个指向同一列表的引用
matrix = [[0] * 3] * 3  # [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
matrix[0][0] = 1
print(matrix)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] ← 意外!

# ✅ 正确:使用列表推导式
matrix = [[0] * 3 for _ in range(3)]
matrix[0][0] = 1
print(matrix)  # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
最佳实践总结
  1. 明确意图:数据不需要修改时,优先使用元组(自文档化)
  2. 性能敏感:大量只读数据使用元组,内存占用更小
  3. 类型提示:使用类型注解明确预期类型
python 复制代码
from typing import List, Tuple

def process_data(items: List[int]) -> Tuple[int, int]:
    return min(items), max(items)  # 返回元组
  1. 字典键:需要复合键时,使用元组而非列表
  2. 解包友好:元组常用于固定数量的返回值

总结

场景 选择 原因
数据会变化 列表 支持动态操作
数据固定不变 元组 性能更好,可哈希,意图明确
需要作为字典键 元组 列表不可哈希
函数返回多个值 元组 Python 默认行为,解包方便
数据量大且只读 元组 内存占用更小,访问更快

记住一句话默认用元组,需要修改时用列表。这会让你的代码更安全、更高效、更易读。

相关推荐
copyer_xyf1 小时前
Python 类全面总结
前端·后端·python
copyer_xyf1 小时前
Python 类型注解:从 TypeScript 迁移理解
前端·后端·python
IT_陈寒1 小时前
React开发实战:从入门到精通
前端·人工智能·后端
西安邮电大学1 小时前
分布式锁三种实现
java·redis·后端·其他·面试
copyer_xyf1 小时前
Python 函数全面总结
前端·后端·python
Gopher_HBo1 小时前
存储技术Redis
后端
xindon121 小时前
go语言项目部署的makefile
开发语言·后端·golang
我是一颗柠檬2 小时前
【Java项目技术亮点】Outbox事件驱动模式:解决分布式事务的终极方案
java·开发语言·分布式·后端·中间件·kafka
那晚的她2 小时前
Scala中Set集合
开发语言·后端·scala