Python基础指南之字典增删改查操作全攻略
一、开篇:字典操作,CRUD是核心
上一篇文章我们学习了字典的创建方式和底层原理。今天我们要掌握使用字典最日常的操作——增删改查(CRUD)。
无论你是在处理用户数据、构建缓存、解析JSON配置还是做数据转换,字典的增删改查都是你每天最频繁的操作。和列表不同,字典的操作围绕键展开——增是通过键赋值,删是按键删除,改是按键更新,查是按键获取。
这篇文章会从最基本的键值操作讲起,深入到各种高级模式:setdefault()的妙用、字典合并的多种姿势、pop()和del的区别、update()的灵活传参,以及各种"看起来能行但不行的"操作和它们的正确替代方式。
二、增——把新数据放进字典
2.1 直接赋值(最常用)
# 最基本的增加:dict[key] = value
person = {}
person['name'] = '小明'
person['age'] = 25
person['city'] = '北京'
print(person) # {'name': '小明', 'age': 25, 'city': '北京'}
# 如果键已存在,则是更新(不会报错)
person['age'] = 26
print(person) # {'name': '小明', 'age': 26, 'city': '北京'}
# 键可以是任何不可变类型
d = {}
d[(1, 2)] = 'point A'
d[42] = 'answer'
print(d) # {(1, 2): 'point A', 42: 'answer'}
2.2 update()——批量添加
person = {'name': '小明'}
# 方式一:用字典update
person.update({'age': 25, 'city': '北京'})
print(person) # {'name': '小明', 'age': 25, 'city': '北京'}
# 方式二:用关键字参数(键必须是合法标识符)
person.update(age=26, job='工程师')
print(person) # {'name': '小明', 'age': 26, 'city': '北京', 'job': '工程师'}
# 方式三:用可迭代对象(每个元素为(key, value)对)
person.update([('hobby', '编程'), ('level', 5)])
print(person)
# {'name': '小明', 'age': 26, 'city': '北京', 'job': '工程师', 'hobby': '编程', 'level': 5}
# 方式四:混合
person.update({'company': 'Tech公司'}, department='研发部')
print(person['company'], person['department']) # Tech公司 研发部
# update()会覆盖已存在的键
person.update({'name': '小红'})
print(person['name']) # 小红(被覆盖了)
2.3 setdefault()——不存在才设置
# setdefault(key, default):
# - 如果key存在:返回已有的值(不修改)
# - 如果key不存在:设置key=default,返回default
# 基本用法
person = {'name': '小明', 'age': 25}
# 'name'存在,返回已有值
name = person.setdefault('name', '未知')
print(name) # 小明(没有被修改)
# 'city'不存在,设置默认值
city = person.setdefault('city', '北京')
print(city) # 北京
print(person) # {'name': '小明', 'age': 25, 'city': '北京'}
# setdefault的经典用法:分组归类
words = ['apple', 'banana', 'apricot', 'cherry', 'blueberry', 'coconut']
by_first = {}
for word in words:
first = word[0]
# 如果first不存在,创建一个空列表;如果存在,返回已有的列表
by_first.setdefault(first, []).append(word)
print(by_first)
# {'a': ['apple', 'apricot'], 'b': ['banana', 'blueberry'], 'c': ['cherry', 'coconut']}
# 另一个经典用法:计数
text = 'hello world hello python world hello'
word_count = {}
for word in text.split():
word_count[word] = word_count.setdefault(word, 0) + 1
print(word_count) # {'hello': 3, 'world': 2, 'python': 1}
# 或更简单的版本(用get)
word_count = {}
for word in text.split():
word_count[word] = word_count.get(word, 0) + 1
setdefault() 和 get() 的区别:get() 只返回默认值但不修改字典,setdefault() 返回默认值的同时会把默认值写入字典。
2.4 字典合并(Python 3.5+)
# 方式一:| 运算符(Python 3.9+,最推荐)
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
merged = d1 | d2
print(merged) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# 有重复键时,右边的覆盖左边的
d3 = {'a': 100, 'e': 5}
merged2 = d1 | d3
print(merged2) # {'a': 100, 'b': 2, 'e': 5}(d3的'a'覆盖了d1的'a')
# 方式二:**解包合并(Python 3.5+)
merged3 = {**d1, **d2, **d3}
print(merged3) # {'a': 100, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
# 方式三:|= 原地合并(Python 3.9+)
d1 = {'a': 1, 'b': 2}
d1 |= {'c': 3, 'a': 100}
print(d1) # {'a': 100, 'b': 2, 'c': 3}
# 方式四:update()(Python 3.0+)
d1.update(d2, d3) # 原地修改
# 注意区别:
# | 和 ** 返回新字典
# |= 和 update() 原地修改现有字典
三、删——从字典中移除数据
3.1 pop()——弹出并返回值
person = {'name': '小明', 'age': 25, 'city': '北京', 'job': '工程师'}
# pop(key):删除键并返回对应的值
job = person.pop('job')
print(job) # 工程师
print(person) # {'name': '小明', 'age': 25, 'city': '北京'}
# pop(key, default):键不存在时返回默认值(不报错)
hobby = person.pop('hobby', '未知')
print(hobby) # 未知
print(person) # 不变
# 不带默认值的pop在键不存在时抛出KeyError
# person.pop('hobby') # KeyError: 'hobby'
3.2 popitem()——弹出一个键值对
person = {'name': '小明', 'age': 25, 'city': '北京'}
# Python 3.7+ popitem()弹出最后插入的键值对(LIFO)
last = person.popitem()
print(last) # ('city', '北京')
print(person) # {'name': '小明', 'age': 25}
# 继续弹出
print(person.popitem()) # ('age', 25)
print(person.popitem()) # ('name', '小明')
# 空字典popitem()抛出KeyError
# {}.popitem() # KeyError: 'popitem(): dictionary is empty'
# popitem()在清理缓存时很有用(逐个处理并移除)
cache = {'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}
while cache:
key, value = cache.popitem()
print(f'处理缓存: {key} = {value}')
3.3 del 语句——按键删除
person = {'name': '小明', 'age': 25, 'city': '北京'}
# del d[key]:删除指定键
del person['city']
print(person) # {'name': '小明', 'age': 25}
# 键不存在时抛出KeyError
# del person['job'] # KeyError: 'job'
# 安全删除
key_to_delete = 'hobby'
if key_to_delete in person:
del person[key_to_delete]
# 或者用pop
person.pop('hobby', None)
# del可以删除整个字典
del person
# print(person) # NameError: name 'person' is not defined
3.4 clear()——清空整个字典
person = {'name': '小明', 'age': 25, 'city': '北京'}
# clear()清空字典
person.clear()
print(person) # {}
# ⚠️ 注意:clear() vs 重新赋值为{}
config = {'host': 'localhost', 'port': 8080}
ref = config # ref指向同一个字典
config = {} # config指向新字典
print(ref) # {'host': 'localhost', 'port': 8080}(ref不受影响)
config = {'host': 'localhost', 'port': 8080}
ref = config
config.clear() # 清空原字典
print(ref) # {}(ref也看到字典被清空了)
3.5 条件删除——按条件过滤
# 删除值小于90的键值对
scores = {'小明': 85, '小红': 92, '小刚': 78, '小李': 95, '小王': 88}
# 方式一:字典推导式(创建新字典,推荐)
high_scores = {k: v for k, v in scores.items() if v >= 90}
print(high_scores) # {'小红': 92, '小李': 95}
# 方式二:原地修改(收集后再删)
scores = {'小明': 85, '小红': 92, '小刚': 78, '小李': 95, '小王': 88}
to_delete = [k for k, v in scores.items() if v < 90]
for k in to_delete:
del scores[k]
print(scores) # {'小红': 92, '小李': 95}
# 删除值类型不满足条件的
mixed = {'a': 1, 'b': 'hello', 'c': 2, 'd': 'world'}
numbers_only = {k: v for k, v in mixed.items() if isinstance(v, int)}
print(numbers_only) # {'a': 1, 'c': 2}
四、改——更新已有的值
4.1 直接赋值改
person = {'name': '小明', 'age': 25, 'city': '北京'}
# 修改单个值
person['age'] = 26
print(person) # {'name': '小明', 'age': 26, 'city': '北京'}
# 修改不存在的键——不是修改,是新增!
person['job'] = '工程师'
print(person) # {'name': '小明', 'age': 26, 'city': '北京', 'job': '工程师'}
# Python没有"修改不存在的键就报错"的语法
# 如果需要这种语义,必须先检查
# if 'key' in d: d['key'] = new_value
4.2 update()——批量修改
person = {'name': '小明', 'age': 25, 'city': '北京'}
# 批量更新
person.update({'age': 26, 'city': '上海', 'job': '工程师'})
print(person)
# {'name': '小明', 'age': 26, 'city': '上海', 'job': '工程师'}
# age和city被更新了,job被新增了
# update也接受关键字参数
person.update(name='小红', hobby='编程')
print(person)
# {'name': '小红', 'age': 26, 'city': '上海', 'job': '工程师', 'hobby': '编程'}
4.3 按条件修改
# 对字典的值做批量变换
prices = {'apple': 10, 'banana': 5, 'orange': 8}
# 所有商品打8折
prices = {k: v * 0.8 for k, v in prices.items()}
print(prices) # {'apple': 8.0, 'banana': 4.0, 'orange': 6.4}
# 只对价格高于7的打8折
prices = {'apple': 10, 'banana': 5, 'orange': 8}
prices = {k: v * 0.8 if v > 7 else v for k, v in prices.items()}
print(prices) # {'apple': 8.0, 'banana': 5, 'orange': 6.4}
# 对键做变换
original = {'user_name': 'xiaoming', 'user_age': '25', 'user_city': 'beijing'}
# 去掉"user_"前缀
cleaned = {k.replace('user_', ''): v for k, v in original.items()}
print(cleaned) # {'name': 'xiaoming', 'age': '25', 'city': 'beijing'}
4.4 将修改表达式直接用于值
# 在现有值的基础上修改
counts = {'a': 1, 'b': 2, 'c': 3}
# 方式一:用get
counts['a'] = counts.get('a', 0) + 1
# 方式二:用collections.Counter
from collections import Counter
counter = Counter(counts)
counter['a'] += 1
counter['d'] += 1 # 不存在的键自动从0开始计数
print(counter) # Counter({'a': 2, 'b': 2, 'c': 3, 'd': 1})
# 方式三:用defaultdict
from collections import defaultdict
# 对值做列表追加
groups = defaultdict(list)
groups['fruit'].append('apple')
groups['fruit'].append('banana')
groups['vegetable'].append('carrot')
print(dict(groups))
# {'fruit': ['apple', 'banana'], 'vegetable': ['carrot']}
# 对值做整数递增
scores = defaultdict(int)
scores['小明'] += 10
scores['小红'] += 20
scores['小明'] += 5
print(dict(scores))
# {'小明': 15, '小红': 20}
五、查——从字典中获取数据
5.1 d[key]——直接访问
person = {'name': '小明', 'age': 25, 'city': '北京'}
# 直接访问——键不存在时抛KeyError
print(person['name']) # 小明
# print(person['job']) # KeyError: 'job'
# 适用场景:确定键一定存在时(如解析已知格式的数据)
5.2 get()——安全的获取
person = {'name': '小明', 'age': 25}
# get(key, default):键不存在时返回默认值
print(person.get('name', '未知')) # 小明
print(person.get('job', '未知')) # 未知
print(person.get('city')) # None(不指定default时默认是None)
# get的经典用法:计数
text = 'hello world hello python world hello'
word_count = {}
for word in text.split():
word_count[word] = word_count.get(word, 0) + 1
print(word_count) # {'hello': 3, 'world': 2, 'python': 1}
# get的嵌套使用——安全访问嵌套字典
config = {
'database': {
'host': 'localhost',
'port': 5432,
}
}
# 不安全的嵌套访问
# port = config['cache']['port'] # KeyError: 'cache'
# 安全的嵌套访问(用get链)
port = config.get('cache', {}).get('port', 6379)
print(port) # 6379(返回默认值)
5.3 in 操作符——检查键是否存在
person = {'name': '小明', 'age': 25}
# 检查键是否存在
print('name' in person) # True
print('job' in person) # False
print('name' not in person) # False
# 检查值是否存在(注意:这是O(n)的!)
print('小明' in person.values()) # True
print(25 in person.values()) # True
# 使用场景
if 'error' in response:
print(f'错误: {response["error"]}')
# 和get()的对比
# in + []:两次查找(先检查,再获取)
if 'name' in person:
name = person['name']
# get():一次查找(更高效)
name = person.get('name', '默认值')
5.4 遍历查找
scores = {'小明': 85, '小红': 92, '小刚': 78, '小李': 95}
# 遍历全部
for name in scores:
print(f'{name}: {scores[name]}分')
# 遍历键值对(推荐)
for name, score in scores.items():
print(f'{name}: {score}分')
# 查找最高分的人
top_student = max(scores, key=scores.get)
print(f'最高分: {top_student} ({scores[top_student]}分)')
# 查找所有及格的人
passing = {name for name, score in scores.items() if score >= 60}
print(f'及格: {passing}')
# 查找特定值的键(反向查找——注意值可能重复)
def find_keys(d, value):
"""找出所有值为value的键"""
return [k for k, v in d.items() if v == value]
d = {'a': 1, 'b': 2, 'c': 1, 'd': 3, 'e': 1}
print(find_keys(d, 1)) # ['a', 'c', 'e']
六、实战综合——用户管理
class UserManager:
"""用字典管理用户数据——综合CRUD操作"""
def __init__(self):
self._users = {} # {user_id: user_data}
def create(self, user_id, name, email, role='user'):
"""增加用户"""
if user_id in self._users:
raise ValueError(f'用户 {user_id} 已存在')
self._users[user_id] = {
'name': name,
'email': email,
'role': role,
'active': True,
'created_at': '2025-06-01',
}
return self._users[user_id]
def read(self, user_id):
"""查询用户"""
user = self._users.get(user_id)
if not user:
return None
return user.copy() # 返回副本,保护内部数据
def update(self, user_id, **kwargs):
"""更新用户——支持关键字参数"""
if user_id not in self._users:
raise KeyError(f'用户 {user_id} 不存在')
self._users[user_id].update(kwargs)
return self._users[user_id]
def delete(self, user_id):
"""删除用户"""
return self._users.pop(user_id, None)
def list_all(self, role=None, active_only=True):
"""列出用户——支持过滤"""
result = {}
for uid, data in self._users.items():
if active_only and not data['active']:
continue
if role and data['role'] != role:
continue
result[uid] = data.copy()
return result
def deactivate(self, user_id):
"""停用用户(软删除)"""
if user_id in self._users:
self._users[user_id]['active'] = False
return True
return False
def count(self):
return len(self._users)
# 使用示例
manager = UserManager()
manager.create(1, '小明', 'xm@test.com', 'admin')
manager.create(2, '小红', 'xh@test.com')
manager.create(3, '小刚', 'xg@test.com', 'editor')
# 查询
print(manager.read(1))
# 更新
manager.update(2, role='moderator', email='xh_new@test.com')
# 删除
manager.delete(3)
# 列出所有
print(f'总共有 {manager.count()} 个用户')
for uid, data in manager.list_all().items():
print(f' [{uid}] {data["name"]} ({data["role"]})')
七、常见错误和陷阱
7.1 运行中修改字典大小
# ⚠️ 经典错误:在遍历字典时修改它
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# 错误写法——会抛出RuntimeError
# for key in d:
# if d[key] % 2 == 0:
# del d[key] # RuntimeError: dictionary changed size during iteration
# 正确方式一:先收集,再删除
to_delete = [k for k, v in d.items() if v % 2 == 0]
for k in to_delete:
del d[k]
print(d) # {'a': 1, 'c': 3}
# 正确方式二:字典推导式(创建新字典)
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
d = {k: v for k, v in d.items() if v % 2 != 0}
print(d) # {'a': 1, 'c': 3}
# 正确方式三:在list(d.keys())上遍历
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
for key in list(d.keys()):
if d[key] % 2 == 0:
del d[key]
print(d) # {'a': 1, 'c': 3}
7.2 可变对象作为默认值的陷阱
# ⚠️ fromkeys的陷阱
# keys = ['a', 'b', 'c']
# d = dict.fromkeys(keys, []) # 所有的键共享同一个列表!
# d['a'].append(1)
# print(d) # {'a': [1], 'b': [1], 'c': [1]} ← 全都变了!
# 正确方式:用推导式
d = {k: [] for k in ['a', 'b', 'c']}
d['a'].append(1)
print(d) # {'a': [1], 'b': [], 'c': []}
7.3 非可哈希的键
# ⚠️ 不能用列表或字典作为键
# d = {}
# d[[1, 2]] = 'value' # TypeError: unhashable type: 'list'
# d[{'a': 1}] = 'value' # TypeError: unhashable type: 'dict'
# 如果确实需要,用元组代替列表
d = {}
d[(1, 2)] = 'value' # 正确
# 用frozenset代替集合
d = {}
d[frozenset([1, 2])] = 'value' # 正确
八、本篇小结
字典CRUD操作核心速查:
增(Create):
d[key] = value—— 直接赋值d.update({...})—— 批量添加d.setdefault(key, default)—— 不存在才设置d1 | d2(Python 3.9+)—— 合并创建新字典
删(Delete):
d.pop(key)—— 弹出值,键不存在抛KeyErrord.pop(key, default)—— 弹出值,键不存在返回默认值d.popitem()—— 弹出最后一个键值对(LIFO)del d[key]—— 按键删除d.clear()—— 清空整个字典
改(Update):
d[key] = new_value—— 直接赋值d.update({...})—— 批量更新- 字典推导式
{k: transform(v) for k, v in d.items()}—— 批量变换
查(Read):
d[key]—— 直接访问(键不存在抛KeyError)d.get(key, default)—— 安全获取key in d—— 检查键是否存在d.keys()/d.values()/d.items()—— 遍历
字典的增删改查看似简单,但 setdefault()、get()、update() 等方法的娴熟运用,能让你的字典操作代码大幅简化。下一篇我们将详细学习字典的keys、values与items遍历的各种技巧。
到此这篇关于Python基础指南之字典增删改查操作全攻略的文章就介绍到这了,更多相关Python字典增删改查操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
python 使用事件对象asyncio.Event来同步协程的操作
这篇文章主要介绍了python 使用事件对象asyncio.Event来同步协程的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-05-05
python使用for...else跳出双层嵌套循环的方法实例
这篇文章主要给大家介绍了关于python使用for...else跳出双层嵌套循环的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用python具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧2020-05-05


最新评论