四:列表、元组、字典、集合——Python 的四种容器

一、回顾与本篇目标

上一篇我们学了条件判断和循环,你的程序已经能“做选择”和“重复做事”了。但有一个问题:如果要处理 100 个学生的成绩,你不可能定义 100 个变量——score1score2score3……那样代码会崩溃。

你需要的是容器——一种能把多个数据打包在一起的数据结构。Python 提供了四种最重要的内置容器:列表、元组、字典、集合。它们各有特点,适用于不同的场景。这一篇把它们全部讲透。

本篇的目标:

  1. 掌握列表的增删改查和切片操作
  2. 理解元组的不可变性及其适用场景
  3. 学会字典的键值对操作
  4. 掌握集合的去重和集合运算
  5. 知道在什么场景用哪种容器

二、列表: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.keyobj['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'] 不能索引
主要用途 有序数据的集合 不应被修改的数据 键值对映射 去重、集合运算

七、选择容器的决策框架

  1. 需要存储一组数据,顺序重要?→ 列表
  2. 数据创建后不需要修改?→ 元组
  3. 需要通过名称(键)来查找数据?→ 字典
  4. 需要去除重复或做集合运算?→ 集合

八、综合演示:学生成绩管理系统

下面这段代码综合运用了本篇学到的四种容器:

# ========== 学生成绩管理系统 ==========

# 用字典存储学生信息(键:学号,值:包含姓名和成绩的字典)
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,定义两个列表 list1list2。用集合运算找出:两个列表的共同元素、只在 list1 中出现的元素、只在 list2 中出现的元素。

练习 4:通讯录

新建 practice4-4.py,用字典做一个通讯录。键是联系人姓名,值是一个包含电话和邮箱的元组。实现添加联系人、查找联系人、删除联系人、显示所有联系人。

十、本篇小结

这一篇你系统学习了 Python 的四种核心容器:

  • 列表 []:有序、可变、可重复。最常用的容器。支持索引、切片、appendinsertremovepopsort 等操作。
  • 元组 ():有序、不可变、可重复。适合表示不应被修改的固定数据。单个元素的元组需要加逗号。元组可以做字典的键。
  • 字典 {key: value}:键值对映射,通过键快速查找值。键不可重复且必须不可变。支持 getkeysvaluesitems 等方法。
  • 集合 {}:无序、可变、不重复。自动去重,支持交并差等集合运算。空集合必须用 set()

这四种容器是你以后每天都要打交道的核心数据结构。把它们练熟,下一篇我们学习函数——如何把一段代码封装起来,让它可以反复调用、传入参数、返回结果。

下一篇预告

下一篇——《函数——定义、参数、返回值》:学习用 def 定义函数、位置参数和关键字参数的区别、默认参数和可变参数、返回单个值和多个值、以及 lambda 匿名函数。同时对比 JavaScript 函数的不同之处。

Python 零基础入门,每周更新。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容