Python 字典(Dictionary)详解

在 Python 中,字典(Dictionary)是一种键值对(key-value) 结构的可变容器,类似于 Java 中的 Map 或 JavaScript 中的对象。它通过键(key)快速查找对应的值(value),是处理关联数据的核心数据结构。本文将详细介绍字典的创建、操作及常用方法。

字典的基本定义

字典使用大括号 {} 定义,键值对之间用逗号分隔,键与值用冒号 : 连接。

# 基本字典(键为字符串,值为整数)
student = {'name': 'Alice', 'age': 20, 'major': 'Computer Science'}

# 键可以是任意不可变类型(如整数、字符串、元组)
mixed_dict = {
    1: 'one',          # 整数作为键
    ('a', 'b'): 123,   # 元组作为键(元组不可变)
    'list': [1, 2, 3]  # 值可以是任意类型(包括列表、字典等)
}

# 空字典
empty_dict = {}
# 用 dict() 函数创建字典
another_dict = dict(name='Bob', age=21)  # 等价于 {'name': 'Bob', 'age': 21}

注意

  • 键必须是不可变类型(如字符串、整数、元组),列表等可变类型不能作为键。
  • 键是唯一的,若重复定义,后面的键值对会覆盖前面的。

字典的核心操作

访问值(通过键)

通过 字典名[键] 访问对应的值,若键不存在则报错。

student = {'name': 'Alice', 'age': 20}

print(student['name'])  # 输出:Alice(访问 name 对应的值)
print(student['age'])   # 输出:20(访问 age 对应的值)

# 使用 get() 方法访问(键不存在时返回默认值,避免报错) 可以通过 'major' in student 来判断该键是否存在
print(student.get('major', 'Unknown'))  # 输出:Unknown(键 'major' 不存在,返回默认值)
添加 / 修改键值对
  • 若键不存在,赋值操作会添加新的键值对。
  • 若键已存在,赋值操作会修改对应的值。
student = {'name': 'Alice', 'age': 20}

# 添加新键值对
student['major'] = 'Math'
print(student)  # 输出:{'name': 'Alice', 'age': 20, 'major': 'Math'}

# 修改已有键的值
student['age'] = 21
print(student)  # 输出:{'name': 'Alice', 'age': 21, 'major': 'Math'}
删除键值对
  • del 字典名[键]:删除指定键值对。
  • 字典名.pop(键):删除并返回指定键的值。
  • 字典名.clear():清空字典所有键值对。
student = {'name': 'Alice', 'age': 21, 'major': 'Math'}

# 删除指定键
del student['major']
print(student)  # 输出:{'name': 'Alice', 'age': 21}

# 删除并返回值
age = student.pop('age')
print(age)      # 输出:21
print(student)  # 输出:{'name': 'Alice'}

# 清空字典
student.clear()
print(student)  # 输出:{}

字典的遍历

字典提供多种方式遍历键、值或键值对:

遍历所有键(keys()
fruit_prices = {'apple': 5, 'banana': 3, 'orange': 4}

# 直接遍历字典(默认遍历键)
for fruit in fruit_prices:
    print(fruit)  # 输出:apple、banana、orange

# 使用 keys() 方法(更清晰)
for fruit in fruit_prices.keys():
    print(fruit)  # 输出同上
遍历所有值(values()
fruit_prices = {'apple': 5, 'banana': 3, 'orange': 4}

for price in fruit_prices.values():
    print(price)  # 输出:5、3、4
遍历键值对(items()
fruit_prices = {'apple': 5, 'banana': 3, 'orange': 4}

# items() 返回包含 (键, 值) 元组的可迭代对象
for fruit, price in fruit_prices.items():
    print(f"水果:{fruit},价格:{price}元")

# 输出:
# 水果:apple,价格:5元
# 水果:banana,价格:3元
# 水果:orange,价格:4元

常用方法速查表

方法 功能描述 时间复杂度 示例
d[key] 访问值(键不存在报错) O(1) value = d['key']
d.get(key[, default]) 安全访问(不存在返回默认值) O(1) d.get('key', 0)
d[key] = value 添加/修改键值对 O(1) d['new'] = 100
d.update(other) 批量更新 O(k) d.update({'a':1, 'b':2})
del d[key] 删除指定键 O(1) del d['key']
d.pop(key[, default]) 删除并返回值 O(1) value = d.pop('key', None)
d.popitem() 删除并返回最后插入的键值对 O(1) k, v = d.popitem()
d.clear() 清空字典 O(n) d.clear()
d.keys() 返回所有键的视图 O(1) for k in d.keys():
d.values() 返回所有值的视图 O(1) for v in d.values():
d.items() 返回所有键值对视图 O(1) for k, v in d.items():
d.copy() 浅拷贝 O(n) new_d = d.copy()
d.setdefault(key[, default]) 获取值,不存在则设置默认值 O(1) d.setdefault('count', 0)
len(d) 返回键值对数量 O(1) size = len(d)
key in d 检查键是否存在 O(1) if 'key' in d:
`d1 d2` (Python 3.9+) 合并字典(返回新字典) O(n+m)
`d1 = d2` (Python 3.9+) 原地合并(类似update) O(m)
示例:update()copy()
# update():合并字典(覆盖重复键)
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict1.update(dict2)
print(dict1)  # 输出:{'a': 1, 'b': 3, 'c': 4}(b 的值被覆盖)

# copy():复制字典
dict3 = dict1.copy()
dict3['a'] = 100
print(dict1)  # 输出:{'a': 1, 'b': 3, 'c': 4}(原字典不变)
print(dict3)  # 输出:{'a': 100, 'b': 3, 'c': 4}(副本被修改)

进阶技巧与实战

实战1:单词计数器
def word_count(text):
    """统计文本中每个单词出现的次数"""
    word_counts = {}
    
    # 方法1:传统方式
    for word in text.lower().split():
        if word in word_counts:
            word_counts[word] += 1
        else:
            word_counts[word] = 1
    
    # 方法2:使用 get()(更简洁)
    # for word in text.lower().split():
    #     word_counts[word] = word_counts.get(word, 0) + 1
    
    # 方法3:使用 defaultdict(最优雅)
    # from collections import defaultdict
    # word_counts = defaultdict(int)
    # for word in text.lower().split():
    #     word_counts[word] += 1
    
    return word_counts

text = "hello world hello python hello world"
print(word_count(text))
# {'hello': 3, 'world': 2, 'python': 1}
实战2:分组与聚合
# 场景:按城市分组用户
users = [
    ('Alice', 'NYC', 25),
    ('Bob', 'LA', 30),
    ('Charlie', 'NYC', 35),
    ('Diana', 'LA', 28),
]

# 分组:城市 → 用户列表
city_users = {}
for name, city, age in users:
    city_users.setdefault(city, []).append({'name': name, 'age': age})

print(city_users)
# {
#     'NYC': [{'name': 'Alice', 'age': 25}, {'name': 'Charlie', 'age': 35}],
#     'LA': [{'name': 'Bob', 'age': 30}, {'name': 'Diana', 'age': 28}]
# }
实战3:字典作为缓存(Memoization)
# 斐波那契数列缓存
fib_cache = {}

def fibonacci(n):
    """带缓存的斐波那契函数(性能提升巨大)"""
    if n in fib_cache:
        return fib_cache[n]
    
    if n <= 1:
        result = n
    else:
        result = fibonacci(n-1) + fibonacci(n-2)
    
    fib_cache[n] = result
    return result

# 对比性能
import time
start = time.time()
print(fibonacci(40))  # 102334155
print(f"耗时:{time.time() - start:.4f}秒")  # 约 0.001 秒(无缓存需 30+ 秒)
实战4:使用字典模拟 switch-case
def switch_example(operation, x, y):
    """使用字典替代多个 if-elif"""
    operations = {
        'add': lambda: x + y,
        'subtract': lambda: x - y,
        'multiply': lambda: x * y,
        'divide': lambda: x / y if y != 0 else float('inf'),
    }
    
    return operations.get(operation, lambda: "Invalid operation")()

print(switch_example('add', 10, 5))       # 15
print(switch_example('multiply', 10, 5))  # 50
print(switch_example('power', 10, 5))     # Invalid operation

常见陷阱与性能优化

🚨 陷阱1:在遍历时修改字典
# ❌ 错误:RuntimeError: dictionary changed size during iteration
d = {'a': 1, 'b': 2, 'c': 3}
# for key in d:
#     if key == 'b':
#         del d[key]  # 报错!

# ✅ 正确:遍历副本
for key in list(d.keys()):  # 转换为列表
    if key == 'b':
        del d[key]
print(d)  # {'a': 1, 'c': 3}
🚨 陷阱2:浅拷贝陷阱
# ❌ 问题:浅拷贝复制的是引用
original = {'list': [1, 2, 3]}
shallow_copy = original.copy()
shallow_copy['list'].append(4)
print(original['list'])  # [1, 2, 3, 4] ← 原字典也被修改!

# ✅ 深拷贝
import copy
original = {'list': [1, 2, 3]}
deep_copy = copy.deepcopy(original)
deep_copy['list'].append(4)
print(original['list'])  # [1, 2, 3] ← 不受影响
🚨 陷阱3:可变对象作为默认值
# ❌ 错误:所有调用共享同一个字典
def add_user(name, user_dict={}):
    user_dict[name] = len(user_dict)
    return user_dict

print(add_user('Alice'))  # {'Alice': 0}
print(add_user('Bob'))    # {'Alice': 0, 'Bob': 1} ← 意外共享!

# ✅ 正确:使用 None 作为哨兵
def add_user(name, user_dict=None):
    if user_dict is None:
        user_dict = {}
    user_dict[name] = len(user_dict)
    return user_dict

print(add_user('Alice'))  # {'Alice': 0}
print(add_user('Bob'))    # {'Bob': 0}

性能优化建议

# 1. 使用 in 代替 has_key()(已废弃)
if 'key' in d:  # ✅ 快速
    pass

# 2. 批量操作使用 update 而非循环
d = {}
new_items = {'a': 1, 'b': 2, 'c': 3}
d.update(new_items)  # ✅ O(k)
# 等价于:
# for k, v in new_items.items():  # ❌ 较慢
#     d[k] = v

# 3. 字典推导式比循环快 30%+
squares = {x: x**2 for x in range(1000)}  # ✅ 快速
# vs
squares = {}                               # ❌ 较慢
# for x in range(1000):
#     squares[x] = x**2

# 4. 预设容量减少扩容(Python 3.7+)
d = {}
d.__sizeof__()  # 初始容量 8
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}  # 会触发扩容

总结与选型指南

何时使用字典?
场景 推荐度 示例
需要通过唯一标识快速查找 ⭐⭐⭐⭐⭐ 用户ID → 用户信息
统计频率/计数 ⭐⭐⭐⭐⭐ 单词出现次数
缓存计算结果 ⭐⭐⭐⭐⭐ 斐波那契数列缓存
配置参数存储 ⭐⭐⭐⭐ config = {'host': 'localhost'}
模拟 switch-case 语句 ⭐⭐⭐⭐ 操作名 → 函数
需要保持顺序且频繁增删(Python 3.7+) ⭐⭐⭐⭐ LRU 缓存
需要嵌套数据结构 ⭐⭐⭐⭐⭐ JSON 解析结果

更多推荐