0%

字典

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

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

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

字典长什么样?怎么建?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 基本字典(键为字符串,值为整数)
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()

1
2
3
4
5
6
7
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() 更安全。

改/增:直接赋值

1
2
3
4
5
6
7
8
9
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():清空字典所有键值对。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 多一个功能——它把删掉的值返回给你,有时候有用。

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

遍历键

1
2
3
4
5
6
7
8
9
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) # 输出同上

遍历值

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

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

遍历键值对(最常用)

1
2
3
4
5
6
7
8
9
10
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:统计词频

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

1
2
3
4
5
6
7
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:按类别分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 可以简化这段代码:

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
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:缓存计算结果(性能优化)

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

1
2
3
4
5
6
7
8
9
10
11
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:遍历的时候删东西

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

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

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

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

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

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

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

1
2
3
4
5
6
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 占位:

1
2
3
4
5
def add_user(name, user_dict=None):
if user_dict is None:
user_dict = {}
user_dict[name] = len(user_dict)
return user_dict

欢迎关注我的其它发布渠道