Python数据结构的完全指南:元组、列表与字典
大家好,今天为大家带来一篇关于Python三种核心数据结构:元组、列表和字典的深度解析。
1. 元组:不可变的有序序列
1.1 什么是元组?
元组(Tuple)是Python中最简单的复合数据结构之一,它是一个不可变的有序值序列。
1.2 创建元组:逗号才是关键!
# 创建元组的各种方式
t1 = (1, 2, 3) # ✅ 常规写法(推荐)
t2 = 1, 2, 3 # ✅ 括号可选
t3 = (1,) # ✅ 单元素必须有逗号
t4 = () # ✅ 空元组
t5 = (1) # ❌ 错误!这是整数1,不是元组
# 验证类型
print(type(t2)) # 输出: <class 'tuple'>
print(type(t5)) # 输出: <class 'int'>(注意:这不是元组!)
核心要点:
- • 逗号创建元组:括号只是可选的包装,真正创建元组的是逗号
- • 单元素陷阱:创建单元素元组时必须加逗号,否则Python会理解为普通变量
- • 为什么推荐加括号:虽然括号可选,但加上括号能让代码更清晰、更易读
1.3 元组的不可变性:既是限制,也是优势
# 元组不可变的特性
coordinates = (10, 20)
# coordinates[0] = 15 # ❌ 这会报错:'tuple' object does not support item assignment
# 但可以重新赋值整个元组
coordinates = (15, 25) # ✅ 这是可以的
不可变性的好处:
- 1. 线程安全:多个线程可以同时访问元组而不需要锁
1.4 空元组的妙用:优雅表示"无结果"
# 数据库查询示例
def find_user(user_id):
"""查找用户信息
user_db是一个字典,保存用户信息
"""
user_db = {
1001: ("张三", 28, "zhangsan@example.com"),
1002: ("李四", 32, "lisi@example.com"),
}
# 使用get()方法,找不到时返回空元组
return user_db.get(user_id, ())
# 简洁明了的使用方式
result = find_user(1001)
if result: # 空元组在布尔上下文中为False
name, age, email = result # 元组解包
print(f"找到用户: {name}, {age}岁, 邮箱: {email}")
else:
print("用户不存在")
为什么用空元组而不用None?
- • 安全遍历:
for item in result: 即使为空也不会出错 - • 类型明确:明确表示"没有数据",而不是"没有返回值"
1.5 元组的常见操作
1. 使用tuple()函数创建元组
letters = tuple("Python") # 输出: ('P', 'y', 't', 'h', 'o', 'n')
2. 获取长度
numbers = (1, 2, 3, 4, 5)
length = len(numbers) # 输出: 5
3. 索引访问(索引从0开始)
values = (1, 3, 5, 7, 9)
second_value = values[1] # 输出: 3
last_value = values[-1] # 输出: 9(负索引从-1开始)
4. 切片操作(左闭右开)
values = (1, 3, 5, 7, 9)
sliced = values[2:4] # 输出: (5, 7)
all_but_first = values[1:] # 输出: (3, 5, 7, 9)
5. 检查元素是否存在
vowels = ("a", "e", "i", "o", "u")
is_o_in = "o" in vowels # 输出: True
6. 遍历元组
for vowel in vowels:
print(f"元音字母: {vowel.upper()}")
输出结果:
元音字母: A
元音字母: E
元音字母: I
元音字母: O
元音字母: U
1.6 元组打包与解包:Python的优雅特性
打包:自动创建元组
coordinates = 4.21, 9.29 # 自动打包成元组 (4.21, 9.29)
解包:将元组值赋给多个变量
x, y = coordinates # x = 4.21, y = 9.29
一行内多变量赋值(先打包再解包)
name, age, occupation = "David", 34, "programmer"
# 等价于:
# temp_tuple = ("David", 34, "programmer")
# name, age, occupation = temp_tuple
交换变量的经典用法(无需临时变量)
a, b = 1, 2
a, b = b, a # a变成2,b变成1
1.7 函数返回多个值:元组的实际应用
def calculate_statistics(numbers):
"""计算一组数字的统计信息"""
if not numbers:
return () # 空元组表示无结果
total = sum(numbers)
average = total / len(numbers)
maximum = max(numbers)
minimum = min(numbers)
# 返回包含多个值的元组
return total, average, maximum, minimum
# 使用函数(建议从这开始读)
data = [10, 20, 30, 40, 50]
stats = calculate_statistics(data)
if stats:
total, avg, max_val, min_val = stats
print(f"总和: {total}, 平均值: {avg}, 最大值: {max_val}, 最小值: {min_val}")
输出结果:
总和: 150, 平均值: 30.0, 最大值: 50, 最小值: 10
2. 列表:灵活的可变序列
2.1 创建列表的三种方式
方式1:列表字面量(最常用)
colors = ["red", "yellow", "green", "blue"]
方式2:使用list()函数转换
numbers = list((1, 2, 3)) # 从元组转换: [1, 2, 3]
方式3:从字符串分割
groceries = "eggs, milk, cheese, bread"
grocery_list = groceries.split(", ") # 输出: ['eggs', 'milk', 'cheese', 'bread']
groceries.split(", ") 方法解析:
这个操作是Python字符串的split()方法,它会根据指定的分隔符将字符串分割成多个部分,并返回一个列表。
执行 split(", ") 后:
- 1. 查找分隔符:在字符串中查找
", "(逗号+空格)
方式4:列表推导式(高级但优雅)
squares = [x**2 for x in range(1, 6)] # 输出: [1, 4, 9, 16, 25]
列表推导式分解:
可以分解为三个部分:
- 1. 输出表达式:
x**2(生成的列表中每个元素的值) - 2. 循环部分:
for x in range(1, 6)(遍历迭代器)
执行过程:
等价于传统的for循环写法:
squares = []
for x in range(1, 6): # x依次取 1, 2, 3, 4, 5
squares.append(x**2) # 将 x的平方 添加到列表
逐步计算:
range(1, 6) 生成: 1, 2, 3, 4, 5
循环次数 | x 的值 | x**2 | 列表变化
--------------------------------
第1次 | 1 | 1 | [1]
第2次 | 2 | 4 | [1, 4]
第3次 | 3 | 9 | [1, 4, 9]
第4次 | 4 | 16 | [1, 4, 9, 16]
第5次 | 5 | 25 | [1, 4, 9, 16, 25]
最终结果:
squares = [1, 4, 9, 16, 25]
2.2 列表基本操作详解
创建示例列表
numbers = [10, 20, 30, 40, 50]
索引访问(索引从0开始)
first = numbers[0] # 10
third = numbers[2] # 30
last = numbers[-1] # 50(负索引从-1开始)
切片操作(左闭右开)
slice1 = numbers[1:4] # [20, 30, 40](包含索引1,不包含索引4)
slice2 = numbers[:3] # [10, 20, 30](从开头到索引2)
slice3 = numbers[2:] # [30, 40, 50](从索引2到结尾)
检查元素存在
is_30_in = 30 in numbers # True
is_60_in = 60 in numbers # False
遍历列表
for number in numbers:
if number > 25: # 条件判断
print(f"大于25的数: {number}")
输出结果:
大于25的数: 30
大于25的数: 40
大于25的数: 50
2.3 修改列表:增删改查
创建初始列表
fruits = ["apple", "banana", "cherry"]
修改元素
fruits[1] = "blueberry" # 修改第二个元素
# 现在fruits是: ["apple", "blueberry", "cherry"]
添加元素
fruits.append("date") # 末尾添加
fruits.insert(1, "apricot") # 在指定位置插入
# 现在fruits是: ["apple", "apricot", "blueberry", "cherry", "date"]
删除元素
# 现在fruits是: ["apple", "apricot", "blueberry", "cherry", "date"]
removed1 = fruits.pop() # 删除并返回最后一个元素: "date"
removed2 = fruits.pop(2) # 删除并返回索引2的元素: "blueberry"
fruits.remove("cherry") # 删除第一个匹配的元素:"cherry"
del fruits[0] # 删除索引0的元素:"apple"
# 现在fruits是: ["apricot"]
扩展列表
fruits.extend(["fig", "grape", "honeydew"])
# 现在fruits是: ["apricot", "fig", "grape", "honeydew"]
注意:remove() 方法只删除第一个匹配的元素。如果要删除所有匹配的元素,建议使用以下方法:
方法一:使用列表推导式(推荐)
fruits = ["apple", "banana", "cherry", "banana", "cherry", "orange", "cherry"]
# 删除所有 "cherry"
fruits = [fruit for fruit in fruits if fruit != "cherry"]
print(fruits) # ['apple', 'banana', 'banana', 'orange']
方法二:使用 while 循环
fruits = ["apple", "banana", "cherry", "banana", "cherry", "orange", "cherry"]
while "cherry" in fruits:
fruits.remove("cherry")
print(fruits) # ['apple', 'banana', 'banana', 'orange']
2.4 列表排序:多种方式详解
sort()函数(改变原列表)
创建示例数据
colors = ["red", "yellow", "green", "blue"]
numbers = [42, 7, 99, 13, 25]
原地排序(修改原列表)
colors.sort() # 默认升序: ['blue', 'green', 'red', 'yellow']
numbers.sort(reverse=True) # 降序: [99, 42, 25, 13, 7]
按字符串长度排序
colors.sort(key=len) # 按长度升序: ['red', 'blue', 'green', 'yellow']
colors.sort(key=len, reverse=True) # 按长度降序: ['yellow', 'green', 'blue', 'red']
sort() 方法的特点:
reverse 参数:
- •
reverse=False(默认):升序排列(从小到大) - •
reverse=True:降序排列(从大到小)
key参数:
sorted()函数(不改变原列表)
# 创建新列表排序(不修改原列表)
colors = ["red", "yellow", "green", "blue"]
sorted_colors = sorted(colors) # 升序排序,返回新列表
sorted_by_len = sorted(colors, key=len) # 按长度排序,返回新列表
print(f"原列表: {colors}")
print(f"排序后新列表: {sorted_colors}")
print(f"按长度排序: {sorted_by_len}")
输出结果:
原列表: ['yellow', 'green', 'blue', 'red']
排序后新列表: ['blue', 'green', 'red', 'yellow']
按长度排序: ['red', 'blue', 'green', 'yellow']
自定义排序函数
# 自定义排序函数
def custom_sort(word):
"""按最后一个字母排序"""
return word[-1]
colors = ["red", "yellow", "green", "blue"]
colors.sort(key=custom_sort)
print(f"按最后一个字母排序: {colors}")
输出结果:
按最后一个字母排序: ['red', 'blue', 'green', 'yellow']
2.5 列表排序总结
| | |
|---|
| .sort() | .sort() |
| .sort(reverse=True) | .sort(reverse=True) |
| .sort(key=函数) | .sort(key=len) |
| .sort(key=函数, reverse=True) | .sort(key=len, reverse=True) |
关键区别:
2.6 列表推导式:简洁高效的数据转换
传统方法 vs 列表推导式对比
传统方法:计算平方数
numbers = [1, 2, 3, 4, 5]
squares_old = []
for num in numbers:
squares_old.append(num**2)
# squares_old: [1, 4, 9, 16, 25]
列表推导式:一行搞定
squares_new = [num**2 for num in numbers] # 输出: [1, 4, 9, 16, 25]
更多列表推导式示例
过滤偶数
even_numbers = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
转换并过滤
words = ["hello", "world", "python", "code"]
upper_long_words = [word.upper() for word in words if len(word) > 4]
# 输出: ['HELLO', 'WORLD', 'PYTHON']
嵌套循环
pairs = [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x != y]
# 输出: [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
2.7 列表复制:浅复制 vs 深复制
错误示例:直接赋值只是引用
list1 = [1, 2, 3]
list2 = list1 # 这只是创建新引用,不是复制!
list2.append(4)
print(f"list1: {list1}") # [1, 2, 3, 4] ❌ list1也被修改了!
print(f"list2: {list2}") # [1, 2, 3, 4]
正确复制:创建真正的副本
方法1:使用切片
list1 = [1, 2, 3]
list2 = list1[:] # 创建浅复制
list2.append(4)
print(f"list1: {list1}") # [1, 2, 3] ✅ list1保持不变
print(f"list2: {list2}") # [1, 2, 3, 4]
方法2:使用list()函数
list3 = list(list1) # 也是浅复制
方法3:使用copy()方法
list4 = list1.copy() # 浅复制
嵌套列表:需要深复制
import copy
nested_list1 = [[1, 2], [3, 4]]
nested_list2 = copy.deepcopy(nested_list1) # 深复制
nested_list2[0][0] = 99
print(f"原列表: {nested_list1}") # [[1, 2], [3, 4]] ✅ 不受影响
print(f"深复制: {nested_list2}") # [[99, 2], [3, 4]]
3. 字典:高效的键值对存储
3.1 创建字典的多种方式
方式1:字典字面量(最常用)
student = {
"name": "张三",
"age": 20,
"major": "计算机科学",
"gpa": 3.8
}
方式2:使用dict()函数
capitals = dict([
("China", "Beijing"),
("USA", "Washington D.C."),
("Japan", "Tokyo")
])
方式3:关键字参数
person = dict(name="李四", age=25, city="上海")
方式4:字典推导式(Python 3.0+)
squares = {x: x**2 for x in range(1, 6)}
# 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
3.2 字典基本操作:增删改查
创建示例字典
# 创建示例字典
contacts = {
"Alice": "alice@example.com",
"Bob": "bob@example.com",
"Charlie": "charlie@example.com"
}
print("初始字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
输出结果:
初始字典:
Alice: alice@example.com
Bob: bob@example.com
Charlie: charlie@example.com
访问值:直接访问
alice_email = contacts["Alice"] # 直接访问
print(f"contacts[\"Alice\"] = {alice_email}")
输出结果:
contacts["Alice"] = alice@example.com
访问值:使用get()方法
bob_email = contacts.get("Bob") # 使用get()方法更安全
print(f"contacts.get(\"Bob\") = {bob_email}")
输出结果:
contacts.get("Bob") = bob@example.com
get()方法的优势:提供默认值
# get()方法的优势:提供默认值
david_email = contacts.get("David", "未找到联系人")
print(f"contacts.get(\"David\", \"未找到联系人\") = {david_email}")
输出结果:
contacts.get("David", "未找到联系人") = 未找到联系人
添加/修改值
print("添加David前的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
contacts["David"] = "david@example.com" # 添加新键值对
print("\n添加David后的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
输出结果:
添加David前的字典:
Alice: alice@example.com
Bob: bob@example.com
Charlie: charlie@example.com
添加David后的字典:
Alice: alice@example.com
Bob: bob@example.com
Charlie: charlie@example.com
David: david@example.com
修改字典
print("\n修改Alice前的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
contacts["Alice"] = "alice_new@example.com" # 修改现有值
print("\n修改Alice后的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
输出结果:
修改Alice前的字典:
Alice: alice@example.com
Bob: bob@example.com
Charlie: charlie@example.com
David: david@example.com
修改Alice后的字典:
Alice: alice_new@example.com
Bob: bob@example.com
Charlie: charlie@example.com
David: david@example.com
删除值
# 删除值
print("\n删除Bob前的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
removed_email = contacts.pop("Bob") # 删除并返回值
print(f"\ncontacts.pop(\"Bob\") 返回: {removed_email}")
print("\n删除Bob后的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
输出结果:
删除Bob前的字典:
Alice: alice_new@example.com
Bob: bob@example.com
Charlie: charlie@example.com
David: david@example.com
contacts.pop("Bob") 返回: bob@example.com
删除Bob后的字典:
Alice: alice_new@example.com
Charlie: charlie@example.com
David: david@example.com
删除字典的最后一个元素
print("\n删除最后一个元素前的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
removed_item = contacts.popitem() # 删除最后一个键值对(Python 3.7+有序)
print(f"\ncontacts.popitem() 返回: {removed_item}")
print("\n删除最后一个元素后的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
输出结果:
删除最后一个元素前的字典:
Alice: alice_new@example.com
Charlie: charlie@example.com
David: david@example.com
contacts.popitem() 返回: ('David', 'david@example.com')
删除最后一个元素后的字典:
Alice: alice_new@example.com
Charlie: charlie@example.com
删除指定键
print("\n删除Charlie前的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
del contacts["Charlie"] # 删除指定键
print("\n删除Charlie后的字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
输出结果:
删除Charlie前的字典:
Alice: alice_new@example.com
Charlie: charlie@example.com
删除Charlie后的字典:
Alice: alice_new@example.com
检查键是否存在
# 检查键是否存在
# 当前字典: Alice: alice_new@example.com
has_alice = "Alice" in contacts # True
has_eve = "Eve" in contacts # False
获取所有键、值、键值对
# 获取所有键、值、键值对
print("\n当前字典:")
for key, value in contacts.items():
print(f" {key}: {value}")
all_names = contacts.keys() # 所有键
all_emails = contacts.values() # 所有值
all_items = contacts.items() # 所有键值对
print(f"\ncontacts.keys() = {list(all_names)}")
print(f"contacts.values() = {list(all_emails)}")
print(f"contacts.items() = {list(all_items)}")
输出结果:
当前字典:
Alice: alice_new@example.com
contacts.keys() = ['Alice']
contacts.values() = ['alice_new@example.com']
contacts.items() = [('Alice', 'alice_new@example.com')]
3.3 遍历字典:多种方式对比
创建示例字典
# 示例字典
student_scores = {
"Alice": 95,
"Bob": 87,
"Charlie": 92,
"David": 78,
"Eve": 88
}
方式1:遍历键(默认方式)
print("1. 所有学生姓名:")
for name in student_scores:
print(f" - {name}")
输出结果:
1. 所有学生姓名:
- Alice
- Bob
- Charlie
- David
- Eve
方式2:遍历键值对(最常用)
print("\n2. 学生姓名和成绩:")
for name, score in student_scores.items():
print(f" - {name}: {score}分")
输出结果:
2. 学生姓名和成绩:
- Alice: 95分
- Bob: 87分
- Charlie: 92分
- David: 78分
- Eve: 88分
方式3:只遍历值
print("\n3. 所有成绩:")
for score in student_scores.values():
print(f" - {score}分")
输出结果:
3. 所有成绩:
- 95分
- 87分
- 92分
- 78分
- 88分
方式4:遍历时获取索引
print("\n4. 带序号的学生列表:")
for i, (name, score) in enumerate(student_scores.items(), 1):
print(f" {i}. {name}: {score}分")
输出结果:
4. 带序号的学生列表:
1. Alice: 95分
2. Bob: 87分
3. Charlie: 92分
4. David: 78分
5. Eve: 88分
关于 enumerate(student_scores.items(), 1) 中的 1:
这个参数表示起始索引值,让索引从 1 开始而不是默认的 0。这样的设计更符合人们的计数习惯。
Python字典遍历的完整总结
核心要点
for item in dict: # item 自动就是键(key)
print(item) # 这是Python的固定规则,不是变量名决定的
原因:Python字典设计时决定,遍历默认返回键,因为键是字典的唯一标识符。
| | | |
|---|
| | | for name in dict: |
| | | for score in dict.values(): |
| | | for name,score in dict.items(): |
3.4 嵌套字典的简单示例
简单例子:班级学生信息
# 班级学生信息(两层嵌套字典)
class_students = {
"张三": {
"年龄": 18,
"成绩": {"语文": 85, "数学": 90, "英语": 88}
},
"李四": {
"年龄": 17,
"成绩": {"语文": 92, "数学": 87}
}
}
三种访问方式
方式1:链式 get()(最安全)
# 获取张三的数学成绩
math_score = class_students.get("张三", {}).get("成绩", {}).get("数学", 0)
print(f"张三的数学成绩: {math_score}") # 输出: 90
# 获取不存在的学生(不会报错)
no_score = class_students.get("王五", {}).get("成绩", {}).get("数学", 0)
print(f"王五的数学成绩: {no_score}") # 输出: 0
链式get()解析:
no_score = class_students.get("王五", {}).get("成绩", {}).get("数学", 0)
步骤分解:
- 1. 第一步:
class_students.get("王五", {})
# 字典中没有"王五"这个键
# 所以返回默认值 {}(空字典)
# 此时结果: {}
# 对空字典{}调用get("成绩", {})
# 空字典中也没有"成绩"这个键
# 所以返回默认值 {}(空字典)
# 此时结果: {}
# 对空字典{}调用get("数学", 0)
# 空字典中没有"数学"这个键
# 所以返回默认值 0
# 最终结果: 0
可视化执行流程:
初始查询: class_students.get("王五", {}).get("成绩", {}).get("数学", 0)
↓
第1步: 在 class_students 中找 "王五" → 不存在 → 返回 {}
↓
第2步: 在 {} 中找 "成绩" → 不存在 → 返回 {}
↓
第3步: 在 {} 中找 "数学" → 不存在 → 返回 0
↓
最终结果: 0
方式2:直接访问(要确保存在)
# 直接访问(确保键存在)
print(f"李四年龄: {class_students['李四']['年龄']}") # 输出: 17
print(f"李四语文成绩: {class_students['李四']['成绩']['语文']}") # 输出: 92
方式3:try-except(处理异常)
try:
english_score = class_students["张三"]["成绩"]["英语"]
print(f"张三的英语成绩: {english_score}") # 输出: 88
except KeyError:
print("信息不存在")
总结对比
简单记忆
- 3. 需要特殊处理错误 → 用 try-except
一句话理解
嵌套字典就是"字典里套字典",访问时要像剥洋葱一样一层层进去,get()方法就像安全绳,防止某一层缺失时摔倒(报错)。
3.5 字典合并与更新
创建示例字典
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
传统方法:update()(修改原字典)
dict1_copy = dict1.copy()
dict1_copy.update(dict2)
print(f"update结果: {dict1_copy}") # {'a': 1, 'b': 3, 'c': 4}
Python 3.9+:合并操作符
merged = dict1 | dict2 # 合并,dict2的值覆盖dict1
print(f"合并结果: {merged}") # {'a': 1, 'b': 3, 'c': 4}
原地合并操作符(Python 3.9+)
dict1 |= dict2 # dict1被修改
print(f"原地合并后dict1: {dict1}") # {'a': 1, 'b': 3, 'c': 4}
总结
本文详细介绍了Python三种核心数据结构:
- 1. 元组:不可变的有序序列,适合存储不会被修改的数据
- 3. 字典:键值对映射,查找高效,适合存储结构化数据
每种数据结构都有其独特的优势和适用场景。理解它们的特点和用法,能够帮助你写出更高效、更优雅的Python代码。
掌握这些数据结构是Python编程的基础,建议多加练习,在实际项目中灵活运用。
📦 资源获取提示
关注「码农自习室」,后台回复关键词 Python学习,即可获取本文完整代码,一起动手掌握高效编程的核心技巧!
❤️ 支持我们
如果觉得本文对你有帮助,欢迎点赞 + 关注,您的支持是我们持续创作优质内容的最大动力!
📚 学习资源说明
本文内容是基于《Python Basics: A Practical Introduction to Python 3》(Real Python)一书的学习笔记整理。
这本书是一本非常优秀的Python入门教材,推荐给所有想要系统学习Python的朋友们。
这本书的特点:
跟着这本书学习,配合我的笔记整理,相信你能更快掌握Python编程!
让我们一起坚持学习,每天进步一点点!💪