05|容器类型详解:列表、字典、元组与集合
场景引入:构建一个内部通讯录系统
你的团队需要一个简易通讯录工具:存储成员的姓名、工号、部门、联系方式,支持按部门筛选、按姓名查找、去除重复记录。单个变量显然无法胜任——你需要容器来批量组织数据。
Python 内置了四种核心容器:列表(list)、元组(tuple)、字典(dict)和集合(set)。理解它们各自的特性和适用场景,是高效处理数据的前提。
列表(list):有序可变序列
列表是 Python 中最通用的容器,支持任意类型的元素,允许重复,且可以随时增删改。
创建列表
# 方括号创建
regions = ['华东', '华南', '华北']
metrics = [100, 200, 300, 400]
mixed_data = ['event_a', 42, True, 9.8]
# 空列表
buffer = []
# 从其他可迭代对象创建
characters = list('deploy') # ['d', 'e', 'p', 'l', 'o', 'y']
sequence = list(range(1, 6)) # [1, 2, 3, 4, 5]
索引与切片
servers = ['web01', 'web02', 'db01', 'cache01', 'worker01']
# 正向索引(从 0 开始)
print(servers[0]) # web01
print(servers[2]) # db01
# 反向索引(从 -1 开始)
print(servers[-1]) # worker01
print(servers[-3]) # db01
# 切片
print(servers[1:3]) # ['web02', 'db01']
print(servers[:2]) # ['web01', 'web02']
print(servers[3:]) # ['cache01', 'worker01']
print(servers[::2]) # ['web01', 'db01', 'worker01'](每隔一个取一个)
print(servers[::-1]) # 反转整个列表
切片遵循”左闭右开”原则:[1:3] 包含索引 1 和 2,不包含 3。
增删改查
nodes = ['alpha', 'beta', 'gamma']
# -- 增 --
nodes.append('delta') # 末尾追加
nodes.insert(1, 'alpha_prime') # 在索引 1 处插入
# -- 删 --
nodes.pop() # 删除末尾元素并返回
nodes.pop(1) # 删除指定索引
nodes.remove('beta') # 按值删除第一个匹配项
del nodes[0] # 直接删除指定索引
# -- 改 --
nodes = ['alpha', 'beta', 'gamma']
nodes[1] = 'bravo' # 直接赋值
# -- 查 --
print('alpha' in nodes) # True
print(nodes.index('gamma')) # 2
print(nodes.count('alpha')) # 1
print(len(nodes)) # 3
排序
latencies = [45, 12, 89, 23, 67, 5]
# sort():原地排序
latencies.sort() # [5, 12, 23, 45, 67, 89]
latencies.sort(reverse=True) # [89, 67, 45, 23, 12, 5]
# sorted():返回新列表
raw = [8, 3, 6, 1]
ordered = sorted(raw) # [1, 3, 6, 8]
print(raw) # [8, 3, 6, 1](原列表不变)
# 自定义排序规则
hostnames = ['worker', 'api', 'gateway', 'db']
hostnames.sort(key=len) # 按长度排序
hostnames.sort(key=str.lower) # 按字母序(忽略大小写)
列表推导式
# 生成立方数
cubes = [n ** 3 for n in range(1, 9)]
# [1, 8, 27, 64, 125, 216, 343, 512]
# 带条件筛选
odd_cubes = [n ** 3 for n in range(1, 9) if n % 2 != 0]
# [1, 27, 125, 343]
# 数据清洗
raw_inputs = [' api ', ' web ', ' db ']
sanitized = [s.strip() for s in raw_inputs]
# ['api', 'web', 'db']
# 条件表达式
tags = ['high' if v > 50 else 'low' for v in [30, 70, 10, 90]]
# ['low', 'high', 'low', 'high']
注意区分:for 后面的 if 是筛选条件(不带 else),for 前面的 if...else 是表达式(必须带 else)。
元组(tuple):有序不可变序列
元组与列表的唯一区别是:创建后不可修改。
# 创建元组
coordinate = (121.47, 31.23)
rgb_white = (255, 255, 255)
# 逗号是关键,括号可省略
dimensions = 1920, 1080, 60
# 单元素元组必须加逗号
singleton = (42,) # 元组
not_a_tuple = (42) # 只是整数 42
# 空元组
empty = ()
# 不可修改
# coordinate[0] = 200 # TypeError
# 支持索引和切片
print(rgb_white[0]) # 255
print(rgb_white[1:]) # (255, 255)
何时选择元组而非列表?
- 数据不应被修改时(如地理坐标、颜色值、版本号)
- 函数返回多个值时,Python 自动将其打包为元组
- 元组属于不可变类型,可用作字典键;列表由于可变性不具备此能力
字典(dict):键值映射
字典用键定位值,类似于查电话号码——你通过姓名(键)找到号码(值)。
创建与访问
# 花括号创建
endpoint = {'host': '10.0.1.5', 'port': 8080, 'protocol': 'https'}
# 访问值
print(endpoint['host']) # 10.0.1.5
print(endpoint['port']) # 8080
# 安全访问:get() 方法
print(endpoint.get('timeout')) # None(键不存在时返回 None)
print(endpoint.get('timeout', 30)) # 30(键不存在时返回默认值)
# 直接用 [] 访问不存在的键会报 KeyError
# print(endpoint['timeout']) # KeyError
# 其他创建方式
cfg = dict(host='10.0.1.5', port=8080)
pairs = dict([('region', 'east'), ('zone', 'a')])
增删改查
settings = {'theme': 'dark', 'lang': 'zh'}
# 增 / 改
settings['font_size'] = 14 # 新增
settings['theme'] = 'light' # 修改
# 删
settings.pop('font_size') # 删除并返回值
del settings['lang'] # 直接删除
# 查
print('theme' in settings) # True
print(len(settings)) # 键值对数量
遍历字典
resource_usage = {'cpu': 78.5, 'memory': 62.3, 'disk': 45.1}
# 遍历键
for metric in resource_usage:
print(metric)
# 遍历值
for value in resource_usage.values():
print(value)
# 遍历键值对(最常用)
for metric, value in resource_usage.items():
print(f'{metric}: {value}%')
# 获取键或值的列表
print(list(resource_usage.keys())) # ['cpu', 'memory', 'disk']
print(list(resource_usage.values())) # [78.5, 62.3, 45.1]
字典推导式
# 根据列表生成字典
services = ['nginx', 'redis', 'postgres']
port_map = {svc: idx + 8000 for idx, svc in enumerate(services)}
# {'nginx': 8000, 'redis': 8001, 'postgres': 8002}
# 数学映射
hex_map = {n: hex(n) for n in range(16)}
实用方法
defaults = {'retry': 3, 'timeout': 30}
# setdefault():键不存在才设置
defaults.setdefault('backoff', 1.5) # 新增
defaults.setdefault('retry', 5) # retry 已存在,不修改
# update():批量更新
defaults.update({'timeout': 60, 'max_conn': 100})
# 字符频率统计(经典用法)
message = "connection refused"
freq = {}
for ch in message:
freq[ch] = freq.get(ch, 0) + 1
print(freq)
**键的类型限制:**字典要求键为不可变类型(如 str、int、tuple),可变类型(如 list)不能充当键。
集合(set):无序且不重复
集合有两大特性:自动去重和无序存储。它特别适合做去重操作和数学集合运算。
创建集合
# 花括号创建
active_zones = {'east', 'west', 'south', 'east'}
print(active_zones) # {'east', 'west', 'south'}(自动去重)
# set() 创建
unique_ids = set([101, 102, 102, 103, 103, 103])
print(unique_ids) # {101, 102, 103}
# 创建空集合必须用 set()
empty_set = set() # 空集合
empty_dict = {} # 这是空字典
增删操作
pool = {1, 2, 3}
pool.add(4) # 添加
pool.add(3) # 已存在,不重复添加
pool.remove(2) # 删除(不存在时报 KeyError)
pool.discard(99) # 删除(不存在时不报错,更安全)
集合运算
backend_team = {'陈工', '王工', '赵工', '孙工'}
frontend_team = {'王工', '赵工', '周工', '吴工'}
# 交集:同时在两个团队的人
overlap = backend_team & frontend_team
print(overlap) # {'王工', '赵工'}
# 并集:在任一团队中的人
all_members = backend_team | frontend_team
print(all_members)
# 差集:只在后端团队的人
backend_only = backend_team - frontend_team
print(backend_only) # {'陈工', '孙工'}
# 对称差集:只在一个团队中的人
exclusive = backend_team ^ frontend_team
print(exclusive) # {'陈工', '孙工', '周工', '吴工'}
实用场景
# 快速去重
raw_tags = ['bug', 'feature', 'bug', 'urgent', 'feature']
unique_tags = list(set(raw_tags))
print(unique_tags)
# 高效成员判断(集合的查找速度远快于列表)
allowed_methods = {'GET', 'POST', 'PUT', 'DELETE'}
if 'PATCH' in allowed_methods:
print("允许")
else:
print("不允许")
# 集合推导式
vowels = {ch for ch in "infrastructure" if ch in "aeiou"}
print(vowels) # {'a', 'i', 'u', 'e'}
嵌套数据结构
实际项目中,容器常常互相嵌套,形成更复杂的数据组织方式。
# 列表嵌套字典:员工记录
team = [
{'emp_id': 'E001', 'name': '陈工', 'skills': ['Python', 'Go']},
{'emp_id': 'E002', 'name': '王工', 'skills': ['JavaScript', 'TypeScript']},
{'emp_id': 'E003', 'name': '赵工', 'skills': ['Python', 'Rust', 'C++']},
]
# 访问赵工的第二项技能
print(team[2]['skills'][1]) # Rust
# 遍历:输出每位成员的技能数量
for member in team:
skill_count = len(member['skills'])
print(f"{member['name']}:掌握 {skill_count} 项技能")
# 字典嵌套字典:多层配置
service_config = {
'database': {
'engine': 'postgresql',
'port': 5432,
'name': 'app_db'
},
'cache': {
'engine': 'redis',
'ttl': 7200
}
}
print(service_config['database']['engine']) # postgresql
解包赋值
Python 支持将序列中的元素拆分赋值给多个变量:
# 元组解包
origin = (116.4, 39.9)
lng, lat = origin
print(lng, lat) # 116.4 39.9
# 列表解包
first, second, third = [10, 20, 30]
# 交换变量
x, y = 1, 2
x, y = y, x
print(x, y) # 2 1
# 用 * 收集剩余元素
leader, *followers = ['alpha', 'beta', 'gamma', 'delta']
print(leader) # alpha
print(followers) # ['beta', 'gamma', 'delta']
front, *body, tail = [1, 2, 3, 4, 5]
print(front) # 1
print(body) # [2, 3, 4]
print(tail) # 5
# 函数返回多值(本质是元组解包)
def get_extremes(dataset):
return min(dataset), max(dataset)
lo, hi = get_extremes([23, 7, 45, 12])
print(f'最小值: {lo}, 最大值: {hi}')
# 遍历时解包
records = [(1, 'deploy'), (2, 'test'), (3, 'release')]
for step, action in records:
print(f'步骤 {step}: {action}')
# enumerate 解包
phases = ['设计', '开发', '测试']
for idx, phase in enumerate(phases):
print(f'{idx}: {phase}')
本章回顾
| 容器 | 符号 | 有序 | 可变 | 重复 | 适用场景 |
|---|---|---|---|---|---|
| 列表 list | [] | 是 | 是 | 允许 | 有序集合、动态数据 |
| 元组 tuple | () | 是 | 否 | 允许 | 不可变数据、函数返回值 |
| 字典 dict | {} | 是* | 是 | 键不重复 | 键值映射、配置数据 |
| 集合 set | {} | 否 | 是 | 不允许 | 去重、集合运算 |
*Python 3.7+ 字典保持插入顺序。
选择原则:
- 有序、允许重复 —> 列表
- 有序、不可修改 —> 元组
- 通过标识查找值 —> 字典
- 去重或集合运算 —> 集合
补充:容器的性能特征
不同容器在不同操作上的性能差异显著,了解这些特征有助于做出正确的选择:
| 操作 | 列表 | 字典 | 集合 |
|---|---|---|---|
| 按索引访问 | 极快 | 不适用 | 不适用 |
按值查找(in) | 慢(逐个检查) | 极快(按键查找) | 极快 |
| 末尾添加 | 极快 | 极快 | 极快 |
| 中间插入 | 慢(需移动元素) | 不适用 | 不适用 |
| 去重 | 需额外操作 | 键自动去重 | 自动去重 |
一个典型场景:如果你需要频繁检查某个元素是否存在,用集合比列表快得多。当数据量达到数万甚至数十万时,这种差异会非常明显。
# 列表查找:O(n) 时间复杂度
allowed_list = list(range(100000))
print(99999 in allowed_list) # 需要遍历整个列表
# 集合查找:O(1) 时间复杂度
allowed_set = set(range(100000))
print(99999 in allowed_set) # 几乎瞬间完成
补充:常见的数据结构操作模式
# 列表去重并保持顺序
def deduplicate(seq):
seen = set()
result = []
for item in seq:
if item not in seen:
seen.add(item)
result.append(item)
return result
print(deduplicate([3, 1, 2, 3, 1, 4])) # [3, 1, 2, 4]
# 按字典值排序
scores = {'alpha': 78, 'beta': 92, 'gamma': 65, 'delta': 88}
ranked = sorted(scores.items(), key=lambda pair: pair[1], reverse=True)
for name, score in ranked:
print(f"{name}: {score}")
掌握这四种容器类型后,你就具备了用 Python 组织和处理各类数据的核心能力。下一章将学习文件读写与异常处理——让程序能够持久化数据并优雅地应对运行时错误。
购买课程解锁全部内容
零基础到独立开发:Python 自动化与 Web 实战
¥29.90