字典

Python 字典:你每天都会用到的”通讯录”

写 Python 代码,字典(Dictionary)是我用得最多的数据结构,没有之一。

它就是一个通讯录:你记一个人的名字(键),就能找到他的电话号码(值)。存什么都能快速找到,这就是字典的价值。

字典长什么样?怎么建?

字典用大括号 {},里面是”键:值”的配对,逗号隔开。

# 基本字典(键为字符串,值为整数)
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}

两个规则记住就行:

  • 键必须是不可变的。字符串、数字、元组都可以,列表不行。因为列表能变,Python 没法保证它”始终能代表同一个东西”。
  • 键不能重复。你如果写了两次同一个键,后面的会覆盖前面的。

增删改查,字典的”四件套”

查:用 [] 或者 get()

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' 不存在,返回默认值)

[]get() 的区别:[] 找不到就报错,程序崩溃;get() 找不到返回默认值,程序还能继续跑。不确定键存不存在的时候,用 get() 更安全。

改/增:直接赋值

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'}

Python 不管你是改还是增,反正 字典[键] = 值 这一句就把键值对放进去了。存在就更新,不存在就新增。

删:del、pop、clear

  • 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)  # 输出:{}

popdel 多一个功能——它把删掉的值返回给你,有时候有用。

遍历字典:取出里面的数据

遍历键

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)  # 输出同上

遍历值

fruit_prices = {'apple': 5, 'banana': 3, 'orange': 4}

for price in fruit_prices.values():
    print(price)  # 输出:5、3、4

遍历键值对(最常用)

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元

几个实战场景,看看字典怎么用

场景1:统计词频

这是字典最经典的应用,没有之一。

text = "hello world hello python hello world"
word_counts = {}

for word in text.lower().split():
    word_counts[word] = word_counts.get(word, 0) + 1

# word_counts = {'hello': 3, 'world': 2, 'python': 1}

get(word, 0) 的意思:如果 word 已经在字典里,返回它的值;如果不在,返回 0。然后加 1 赋值回去。一行搞定统计,比用 if-else 简洁多了。

场景2:按类别分组

users = [
    ('Alice', 'NYC', 25),
    ('Bob', 'LA', 30),
    ('Charlie', 'NYC', 35),
]

city_users = {}
for name, city, age in users:
    if city not in city_users:
        city_users[city] = []
    city_users[city].append({'name': name, 'age': age})

# city_users = {
#     'NYC': [{'name': 'Alice', 'age': 25}, {'name': 'Charlie', 'age': 35}],
#     'LA': [{'name': 'Bob', 'age': 30}]
# }

setdefault 可以简化这段代码:

city_users.setdefault(city, []).append({'name': name, 'age': age})

setdefault 的意思是:如果 city 存在,返回它的值;如果不存在,用第二个参数创建默认值并返回。一行代替 if-else 判断。

场景3:用字典替代多个 if-elif

写代码的时候经常遇到一堆 if-elif,看着烦。可以用字典把条件和操作映射起来:

def add(x, y): return x + y
def sub(x, y): return x - y
def mul(x, y): return x * y

operations = {
    'add': add,
    'subtract': sub,
    'multiply': mul,
}

result = operations.get('add', lambda x,y: None)(10, 5)   # 15

这个技巧在处理命令分发、路由选择的时候很好用,代码比一堆 if-elif 干净多了。

场景4:缓存计算结果(性能优化)

计算斐波那契数列的时候,不用缓存会重复算很多次。用字典把算过的结果记下来:

cache = {}

def fib(n):
    if n in cache:
        return cache[n]
    if n <= 1:
        result = n
    else:
        result = fib(n-1) + fib(n-2)
    cache[n] = result
    return result

没缓存的时候算 fib(40) 可能要 30 秒,有缓存 0.001 秒。差别巨大。


几个容易踩的坑

坑1:遍历的时候删东西

d = {'a': 1, 'b': 2, 'c': 3}
for key in d:
    if key == 'b':
        del d[key]   # RuntimeError! 遍历过程中不能修改字典大小

正确做法:遍历字典的副本(转换成列表再遍历):

for key in list(d.keys()):
    if key == 'b':
        del d[key]

坑2:浅拷贝只复制了外层

original = {'list': [1, 2, 3]}
copy = original.copy()
copy['list'].append(4)
print(original['list'])   # [1, 2, 3, 4] 原字典也被改了!

dict.copy() 是浅拷贝,里面的列表还是同一个引用。想完全独立用 copy.deepcopy()

坑3:可变对象当默认参数

def add_user(name, user_dict={}):
    user_dict[name] = len(user_dict)
    return user_dict

add_user('Alice')   # {'Alice': 0}
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