跳到主要内容
预计阅读 25 分钟

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