一、回顾与本篇目标
上一篇我们学了条件判断和循环,你的程序已经能“做选择”和“重复做事”了。但有一个问题:如果要处理 100 个学生的成绩,你不可能定义 100 个变量——score1、score2、score3……那样代码会崩溃。
你需要的是容器——一种能把多个数据打包在一起的数据结构。Python 提供了四种最重要的内置容器:列表、元组、字典、集合。它们各有特点,适用于不同的场景。这一篇把它们全部讲透。
本篇的目标:
- 掌握列表的增删改查和切片操作
- 理解元组的不可变性及其适用场景
- 学会字典的键值对操作
- 掌握集合的去重和集合运算
- 知道在什么场景用哪种容器
二、列表:Python 中最常用的容器
列表是 Python 中最灵活的数据结构。它相当于 JavaScript 的数组——用方括号 [] 包裹,元素用逗号分隔,可以存放任意类型的数据,可以随时增删改。
创建列表
# 空列表
empty = []
# 存放同类型数据
numbers = [1, 2, 3, 4, 5]
fruits = ['苹果', '香蕉', '橘子']
# 存放不同类型的数据(Python 允许,但不推荐混放)
mixed = [42, 'hello', True, 3.14]
# 用 list() 函数从其他可迭代对象创建
chars = list('hello') # ['h', 'e', 'l', 'l', 'o']
range_list = list(range(5)) # [0, 1, 2, 3, 4]
访问元素:索引和切片
列表的索引和切片规则和字符串完全一样:
fruits = ['苹果', '香蕉', '橘子', '葡萄', '西瓜']
# 索引(从 0 开始,支持负数)
print(fruits[0]) # '苹果'
print(fruits[-1]) # '西瓜' —— 最后一个
print(fruits[-2]) # '葡萄' —— 倒数第二个
# 切片 [开始:结束:步长]
print(fruits[1:3]) # ['香蕉', '橘子'] —— 索引 1 到 2(不包括 3)
print(fruits[:3]) # ['苹果', '香蕉', '橘子'] —— 前三个
print(fruits[2:]) # ['橘子', '葡萄', '西瓜'] —— 从索引 2 到末尾
print(fruits[::2]) # ['苹果', '橘子', '西瓜'] —— 每隔一个取一个
print(fruits[::-1]) # ['西瓜', '葡萄', '橘子', '香蕉', '苹果'] —— 反转列表
列表的增删改查
添加元素
fruits = ['苹果', '香蕉']
# append:在末尾添加一个元素
fruits.append('橘子')
print(fruits) # ['苹果', '香蕉', '橘子']
# insert:在指定位置插入
fruits.insert(1, '葡萄')
print(fruits) # ['苹果', '葡萄', '香蕉', '橘子']
# extend:把另一个列表的所有元素追加到末尾
fruits.extend(['西瓜', '草莓'])
print(fruits) # ['苹果', '葡萄', '香蕉', '橘子', '西瓜', '草莓']
删除元素
fruits = ['苹果', '香蕉', '橘子', '香蕉']
# remove:删除第一个匹配的值
fruits.remove('香蕉')
print(fruits) # ['苹果', '橘子', '香蕉'] —— 只删了第一个香蕉
# pop:删除并返回指定位置的元素(默认最后一个)
last = fruits.pop()
print(last) # '香蕉'
print(fruits) # ['苹果', '橘子']
first = fruits.pop(0)
print(first) # '苹果'
print(fruits) # ['橘子']
# del:根据索引删除(不返回值)
nums = [10, 20, 30, 40]
del nums[1]
print(nums) # [10, 30, 40]
# clear:清空整个列表
nums.clear()
print(nums) # []
修改元素
fruits = ['苹果', '香蕉', '橘子']
fruits[1] = '草莓'
print(fruits) # ['苹果', '草莓', '橘子']
查找元素
fruits = ['苹果', '香蕉', '橘子', '香蕉']
# index:查找元素第一次出现的位置
print(fruits.index('香蕉')) # 1
# count:统计元素出现次数
print(fruits.count('香蕉')) # 2
# in:判断元素是否存在
print('橘子' in fruits) # True
print('西瓜' in fruits) # False
print('西瓜' not in fruits) # True
列表的其他常用操作
nums = [3, 1, 4, 1, 5, 9, 2]
# 排序
nums.sort() # 原地排序,改变原列表
print(nums) # [1, 1, 2, 3, 4, 5, 9]
nums.sort(reverse=True) # 降序
print(nums) # [9, 5, 4, 3, 2, 1, 1]
# 不改变原列表的排序
original = [3, 1, 4, 1, 5]
sorted_list = sorted(original) # 返回新列表
print(original) # [3, 1, 4, 1, 5] —— 没变
print(sorted_list) # [1, 1, 3, 4, 5]
# 反转
fruits = ['苹果', '香蕉', '橘子']
fruits.reverse()
print(fruits) # ['橘子', '香蕉', '苹果']
# 长度
print(len(fruits)) # 3
# 最大值和最小值
print(max(nums)) # 9
print(min(nums)) # 1
print(sum(nums)) # 求和
列表和 JavaScript 数组的对比
| 操作 | JavaScript | Python |
|---|---|---|
| 末尾添加 | arr.push(item) |
list.append(item) |
| 末尾删除 | arr.pop() |
list.pop() |
| 指定位置插入 | arr.splice(index, 0, item) |
list.insert(index, item) |
| 根据值删除 | arr.splice(arr.indexOf(val), 1) |
list.remove(val) |
| 合并数组 | arr.concat(arr2) |
list.extend(list2) |
| 判断存在 | arr.includes(val) |
val in list |
| 长度 | arr.length |
len(list) |
列表的遍历
fruits = ['苹果', '香蕉', '橘子']
# 方式一:直接遍历元素
for fruit in fruits:
print(fruit)
# 方式二:遍历索引和元素
for i, fruit in enumerate(fruits):
print(f'第{i + 1}个:{fruit}')
# 方式三:用索引遍历
for i in range(len(fruits)):
print(fruits[i])
三、元组:不可变的列表
元组看起来和列表几乎一样,唯一的区别是元组创建后不能修改——不能添加、删除、修改元素。元组用小括号 () 包裹。
创建元组
# 创建元组
point = (3, 4)
person = ('张三', 28, '上海')
# 单个元素的元组(注意逗号不能省略!)
single = (42,) # 这是一个元组
not_a_tuple = (42) # 这是数字 42,不是元组!
# 空元组
empty = ()
# 不用括号也可以(但可读性差,不推荐)
point = 3, 4 # 也是元组
访问元组元素
和列表一样,支持索引和切片:
person = ('张三', 28, '上海')
print(person[0]) # '张三'
print(person[-1]) # '上海'
print(person[:2]) # ('张三', 28)
不能修改元组
point = (3, 4)
# point[0] = 10 # 报错!TypeError: 'tuple' object does not support item assignment
# point.append(5) # 报错!元组没有 append 方法
元组的“可变”陷阱
元组本身不可变,但如果元组里的元素是可变对象(如列表),那个可变对象的内容是可以改变的:
# 元组里有一个列表
data = (1, 2, [3, 4])
# data[2] = [5, 6] # 报错!不能修改元组中的引用
# 但是可以修改那个列表的内容
data[2].append(5)
print(data) # (1, 2, [3, 4, 5]) —— 元组本身没变,但列表变了
为什么需要元组?
- 表示不应该被修改的数据:比如坐标、RGB 颜色值、星期几——这些数据不应该被改。
- 作为字典的键:字典的键必须是不可变的,元组可以当键,列表不行。
- 函数返回多个值:Python 函数可以返回多个值,实际返回的是一个元组。
- 性能更好:元组比列表占更少内存,创建更快。
# 元组用作字典的键
locations = {
(30.25, 120.16): '杭州',
(39.90, 116.40): '北京',
}
print(locations[(39.90, 116.40)]) # '北京'
# 函数返回多个值(实际是返回元组)
def get_user_info():
return '张三', 28, '上海'
name, age, city = get_user_info() # 解包
print(name, age, city)
四、字典:键值对的容器
字典是 Python 中最重要的数据结构之一。它相当于 JavaScript 中的对象(Object)或 Map——用键来查找对应的值。字典用花括号 {} 包裹,每个元素是一个 键: 值 对。
创建字典
# 空字典
empty = {}
# 创建字典
person = {
'name': '张三',
'age': 28,
'city': '上海'
}
# 用 dict() 函数创建
person2 = dict(name='李四', age=22, city='北京')
# 从键值对列表创建
pairs = [('name', '王五'), ('age', 25), ('city', '广州')]
person3 = dict(pairs)
访问和修改字典
person = {'name': '张三', 'age': 28}
# 通过键访问值
print(person['name']) # '张三'
# 如果键不存在,用 [] 访问会报错
# print(person['email']) # KeyError!
# get 方法:键不存在时返回 None 或自定义默认值
print(person.get('email')) # None
print(person.get('email', '未知')) # '未知'
# 添加或修改键值对
person['city'] = '上海' # 添加新键
person['age'] = 29 # 修改已有键的值
print(person) # {'name': '张三', 'age': 29, 'city': '上海'}
字典的增删查操作
person = {'name': '张三', 'age': 28, 'city': '上海'}
# 删除键值对
del person['city']
age = person.pop('age') # 删除并返回被删除的值
print(person) # {'name': '张三'}
# 检查键是否存在
print('name' in person) # True
print('email' in person) # False
# 获取所有键
print(person.keys()) # dict_keys(['name'])
print(list(person.keys())) # ['name'] —— 转成列表
# 获取所有值
print(person.values()) # dict_values(['张三'])
# 获取所有键值对
print(person.items()) # dict_items([('name', '张三')])
字典的遍历
person = {'name': '张三', 'age': 28, 'city': '上海'}
# 遍历键
for key in person:
print(key, person[key])
# 遍历键值对
for key, value in person.items():
print(f'{key}: {value}')
字典和 JavaScript 对象的对比
| 操作 | JavaScript | Python |
|---|---|---|
| 访问属性 | obj.key 或 obj['key'] |
dict['key'](不能用 dict.key) |
| 安全访问 | obj?.key |
dict.get('key', default) |
| 检查键 | 'key' in obj |
'key' in dict |
| 删除 | delete obj.key |
del dict['key'] |
| 遍历键 | Object.keys(obj) |
dict.keys() |
| 遍历键值对 | Object.entries(obj) |
dict.items() |
五、集合:自动去重的容器
集合是 Python 中用于去重和数学集合运算的数据结构。它和列表最大的区别是:集合中的元素是唯一的,不允许重复。集合也用花括号 {} 包裹,但和字典的区别是:集合只有值,没有键值对。
创建集合
# 创建集合(用花括号)
fruits = {'苹果', '香蕉', '橘子'}
# 空集合必须用 set(),不能用 {}(因为 {} 是空字典)
empty = set()
# 从列表创建(自动去重)
nums = set([1, 2, 2, 3, 3, 3])
print(nums) # {1, 2, 3} —— 重复的自动去掉了
集合的增删操作
fruits = {'苹果', '香蕉'}
# 添加元素
fruits.add('橘子')
print(fruits) # {'苹果', '香蕉', '橘子'}
# 添加重复元素——没有效果
fruits.add('苹果')
print(fruits) # {'苹果', '香蕉', '橘子'} —— 没变
# 删除元素
fruits.remove('香蕉') # 如果元素不存在会报错
fruits.discard('西瓜') # 如果元素不存在,不报错
# 随机删除一个元素
item = fruits.pop()
print(item)
集合运算:交、并、差
这是集合最强大的功能——可以做数学中的集合运算:
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
# 并集:两个集合的所有元素(去重)
print(a | b) # {1, 2, 3, 4, 5, 6}
print(a.union(b)) # 同上
# 交集:两个集合共有的元素
print(a & b) # {3, 4}
print(a.intersection(b)) # 同上
# 差集:只在 a 中、不在 b 中的元素
print(a - b) # {1, 2}
print(a.difference(b)) # 同上
# 对称差集:只在其中一个集合中,不在两个都在的元素
print(a ^ b) # {1, 2, 5, 6}
print(a.symmetric_difference(b)) # 同上
集合的常见用途
# 1. 列表去重
nums = [1, 2, 2, 3, 3, 3, 4]
unique_nums = list(set(nums))
print(unique_nums) # [1, 2, 3, 4](顺序可能改变)
# 2. 判断元素是否存在(集合比列表快)
vip_users = {'张三', '李四', '王五'}
print('张三' in vip_users) # True —— 比在列表中查找快得多
# 3. 找两个列表的共同元素
list1 = [1, 2, 3, 4]
list2 = [3, 4, 5, 6]
common = list(set(list1) & set(list2))
print(common) # [3, 4]
六、四种容器对比总结
| 特性 | 列表 | 元组 | 字典 | 集合 |
|---|---|---|---|---|
| 写法 | [1, 2, 3] |
(1, 2, 3) |
{'a': 1, 'b': 2} |
{1, 2, 3} |
| 是否可变 | ✅ 可变 | ❌ 不可变 | ✅ 可变 | ✅ 可变 |
| 是否有序 | ✅ 有序 | ✅ 有序 | ✅ 有序(Python 3.7+) | ❌ 无序 |
| 元素是否可重复 | ✅ 可重复 | ✅ 可重复 | 键不可重复 | ❌ 不可重复 |
| 访问方式 | 索引 [0] |
索引 [0] |
键 ['key'] |
不能索引 |
| 主要用途 | 有序数据的集合 | 不应被修改的数据 | 键值对映射 | 去重、集合运算 |
七、选择容器的决策框架
- 需要存储一组数据,顺序重要?→ 列表
- 数据创建后不需要修改?→ 元组
- 需要通过名称(键)来查找数据?→ 字典
- 需要去除重复或做集合运算?→ 集合
八、综合演示:学生成绩管理系统
下面这段代码综合运用了本篇学到的四种容器:
# ========== 学生成绩管理系统 ==========
# 用字典存储学生信息(键:学号,值:包含姓名和成绩的字典)
students = {}
# 添加学生
def add_student(student_id, name):
if student_id in students:
print(f'学号 {student_id} 已存在')
else:
students[student_id] = {
'name': name,
'scores': [] # 用列表存储成绩
}
print(f'学生 {name} 添加成功')
# 录入成绩
def add_score(student_id, score):
if student_id not in students:
print(f'学号 {student_id} 不存在')
else:
students[student_id]['scores'].append(score)
print(f'成绩 {score} 录入成功')
# 计算平均分
def get_average(student_id):
if student_id not in students:
return None
scores = students[student_id]['scores']
if len(scores) == 0:
return 0
return sum(scores) / len(scores)
# 获取所有不重复的科目(用集合去重——这里用分数模拟)
def get_all_scores():
all_scores = set()
for student in students.values():
for score in student['scores']:
all_scores.add(score)
return all_scores
# 测试
add_student('001', '张三')
add_student('002', '李四')
add_student('003', '王五')
add_score('001', 85)
add_score('001', 92)
add_score('002', 78)
add_score('003', 88)
add_score('003', 95)
print('\n===== 成绩统计 =====')
for student_id, info in students.items():
avg = get_average(student_id)
print(f'{info["name"]}(学号{student_id}):{info["scores"]},平均分:{avg:.1f}')
print(f'所有成绩:{get_all_scores()}')
代码中使用的容器:
- 字典
students:用学号快速查找学生信息。 - 列表
scores:存储每个学生的多门成绩,可以动态添加。 - 集合
all_scores:自动去重,得到所有不重复的成绩。 - 元组:没有直接用到,但
dict.items()返回的每个键值对本质上就是元组。
九、本篇动手练习
练习 1:购物清单管理
新建 practice4-1.py,用列表实现购物清单。用户可以输入商品名称来添加,输入“删除”加商品名来删除,输入“列表”查看所有商品,输入“退出”结束程序。
练习 2:字母频率统计
新建 practice4-2.py,让用户输入一段文字,用字典统计每个字母出现的次数。最后按字母顺序输出统计结果。提示:用 sorted() 对字典的键排序。
练习 3:找两个列表的差异
新建 practice4-3.py,定义两个列表 list1 和 list2。用集合运算找出:两个列表的共同元素、只在 list1 中出现的元素、只在 list2 中出现的元素。
练习 4:通讯录
新建 practice4-4.py,用字典做一个通讯录。键是联系人姓名,值是一个包含电话和邮箱的元组。实现添加联系人、查找联系人、删除联系人、显示所有联系人。
十、本篇小结
这一篇你系统学习了 Python 的四种核心容器:
- 列表
[]:有序、可变、可重复。最常用的容器。支持索引、切片、append、insert、remove、pop、sort等操作。 - 元组
():有序、不可变、可重复。适合表示不应被修改的固定数据。单个元素的元组需要加逗号。元组可以做字典的键。 - 字典
{key: value}:键值对映射,通过键快速查找值。键不可重复且必须不可变。支持get、keys、values、items等方法。 - 集合
{}:无序、可变、不重复。自动去重,支持交并差等集合运算。空集合必须用set()。
这四种容器是你以后每天都要打交道的核心数据结构。把它们练熟,下一篇我们学习函数——如何把一段代码封装起来,让它可以反复调用、传入参数、返回结果。
下一篇预告
下一篇——《函数——定义、参数、返回值》:学习用 def 定义函数、位置参数和关键字参数的区别、默认参数和可变参数、返回单个值和多个值、以及 lambda 匿名函数。同时对比 JavaScript 函数的不同之处。
Python 零基础入门,每周更新。













暂无评论内容