← 返回题目列表

Python 字典的 keys()/values()/items() 返回什么?字典怎么合并?

中等 第 21 / 21 题 更新于 2026/07/31
Python字典视图字典合并dict

简化版

**dict.keys()dict.values()dict.items() 返回的不是列表,而是「视图对象(view object)」——它们是字典的「动态窗口」,字典变化时视图会自动跟着变,而且几乎不占内存(不复制数据)。**关键点:① 视图是动态的——keys = d.keys() 之后往 d 里加键,keys 也会随之出现新键(它是活的引用,不是快照);② 视图支持集合运算——d1.keys() & d2.keys()(两字典的公共键)、|(并集)、-(差集),像集合一样操作(items 视图在值可哈希时也支持);③ 视图不能索引d.keys()[0] 报错,要先 list(d.keys()))。字典合并的几种方式:① {**d1, **d2}(解包合并,3.5+,后者覆盖前者);② d1 | d2(合并运算符,3.9+,返回新字典)、d1 |= d2(原地更新);③ d1.update(d2)(原地更新 d1)。核心记忆:keys/values/items 返回动态视图(跟字典变、省内存、支持集合运算、不能索引),合并用 {**d1,**d2}d1|d2(3.9+)或 update(原地)。

详细版

视图对象与合并

操作说明
d.keys()键视图(动态、支持集合运算)
d.values()值视图(动态、不支持集合运算)
d.items()键值对视图(动态、值可哈希时支持集合运算)
{**d1, **d2}解包合并(3.5+,返回新字典)
d1 | d2合并运算符(3.9+,返回新字典)
d1 |= d2 / d1.update(d2)原地更新 d1
d = {'a': 1, 'b': 2}

# 视图对象(不是列表)
ks = d.keys()
print(ks)               # dict_keys(['a', 'b'])
print(type(ks))         # <class 'dict_keys'>

# 动态:字典变,视图跟着变
d['c'] = 3
print(ks)               # dict_keys(['a', 'b', 'c'])  ← 自动多了 c!

# 集合运算(键视图)
d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 4}
print(d1.keys() & d2.keys())   # {'b'}(公共键)
print(d1.keys() | d2.keys())   # {'a','b','c'}(所有键)
print(d1.keys() - d2.keys())   # {'a'}(d1 独有)

# 不能索引,要先转 list
# d.keys()[0]           # ✗ TypeError
list(d.keys())[0]       # ✓

# 字典合并
d1 = {'a': 1, 'b': 2}
d2 = {'b': 99, 'c': 3}
print({**d1, **d2})     # {'a':1, 'b':99, 'c':3}(d2 覆盖 d1 的 b)
print(d1 | d2)          # 同上(3.9+)
d1 |= d2                # 原地更新 d1
d1.update(d2)           # 原地更新(等价)

# 遍历 items 是最常用的
for k, v in d.items():
    print(k, v)

⚠️ 两个核心认知:① keys()/values()/items() 是「视图」不是「列表」——它是字典的活窗口(字典改,视图跟着改)、几乎零内存开销(不复制),而且 keys 视图能当集合用做交并差;② 合并字典优先用 d1 | d2(3.9+,清晰)或 {**d1, **d2}(兼容老版本),冲突时「后面的覆盖前面的」。视图的「动态性」既是优点也是坑:优点是省内存、总反映最新状态;坑是「遍历字典时不能改字典大小」——for k in d.keys(): d[新键]=...del d[k] 会因为视图在遍历中变化而抛 RuntimeError: dictionary changed size during iteration。要边遍历边改,先固化成列表 for k in list(d.keys())。集合运算是视图的隐藏利器:求两个字典的公共键 d1.keys() & d2.keys()、只在 d1 不在 d2 的键 d1.keys() - d2.keys(),一行搞定,比手写循环优雅。

完整版教学

一、视图对象是什么

先理解视图对象的本质:

dict.keys()/values()/items() 返回"视图对象":
  dict_keys、dict_values、dict_items
  → 不是列表!是字典的"动态视图"

三个视图:
  d.keys()   → 所有键的视图
  d.values() → 所有值的视图
  d.items()  → 所有 (键,值) 元组的视图

视图的两大特性:
  ① 动态(live):反映字典的当前状态
     字典变化 → 视图自动跟着变(不是快照)
  ② 轻量:几乎不占内存
     不复制数据,只是"指向"字典的窗口

对比 Python 2:
  Py2 的 keys()/values()/items() 返回"列表"(复制、快照)
  Py3 改成"视图"(动态、省内存)
  → Py2 迁 Py3 时,需要列表的地方要 list(d.keys())

能做什么:
  ① 迭代:for k in d.keys()(或直接 for k in d)
  ② len:len(d.keys())
  ③ 成员判断:'a' in d.keys()(也可直接 'a' in d)
  ④ 集合运算:keys/items 支持 & | - ^
  ⑤ 转列表:list(d.keys())

不能做:
  ✗ 索引:d.keys()[0](视图不支持下标)
  ✗ 修改:视图是只读的(改要改字典本身)

所以视图对象(dict_keys 等)是字典的动态窗口:跟字典变+省内存,不是列表

dict.keys()/values()/items() 返回「视图对象」(dict_keys、dict_values、dict_items)——不是列表、是字典的「动态视图」。三个视图:keys(所有键)、values(所有值)、items(所有键值对元组)。视图的两大特性① 动态(live)——反映字典当前状态、字典变化视图自动跟着变(不是快照);② 轻量——几乎不占内存、不复制数据、只是指向字典的窗口。对比 Python 2:Py2 返回列表(复制、快照)、Py3 改成视图(动态、省内存),Py2 迁 Py3 时需要列表的地方要 list(d.keys())能做:迭代、len、成员判断、集合运算(keys/items)、转列表。不能做:索引(d.keys()[0] 不支持下标)、修改(只读)。理解「视图对象(dict_keys/values/items)是字典的动态窗口:①动态(跟字典变、不是快照)②轻量(不复制省内存);Py2 返回列表、Py3 改视图是迁移坑;能迭代/len/集合运算、不能索引/改」,就理解了视图对象。

二、视图的动态性

理解「动态」的表现与坑:

动态性表现:视图始终反映字典的当前状态

  d = {'a': 1}
  ks = d.keys()      # ks 是视图
  print(ks)          # dict_keys(['a'])
  d['b'] = 2         # 改字典
  print(ks)          # dict_keys(['a', 'b'])  ← ks 自动更新!
  del d['a']
  print(ks)          # dict_keys(['b'])

  → ks 不是拍了张照片,而是"活的引用"

好处:
  ① 省内存(不复制)
  ② 总是最新(不用重新取)

坑:遍历时改字典大小 → RuntimeError
  for k in d.keys():
      d[k*2] = 0        # ✗ 遍历中改变字典大小
  → RuntimeError: dictionary changed size during iteration
  del 也一样:
  for k in d.keys():
      if cond: del d[k] # ✗ 同样报错

  原因:视图动态,遍历中字典结构变了 → 迭代器失效

解决:先固化成列表(快照)
  for k in list(d.keys()):   # list() 复制一份,遍历它
      if cond: del d[k]      # ✓ 改的是 d,遍历的是快照
  或:先收集要删的,遍历后统一删
  to_del = [k for k in d if cond]
  for k in to_del: del d[k]

改值(不改大小)是允许的:
  for k in d:
      d[k] = d[k] * 2     # ✓ 只改值,大小不变,OK

所以视图动态(跟字典变):好处省内存/最新,坑遍历中改大小报错(先 list()固化)

动态性表现:视图始终反映字典的当前状态——ks = d.keys()d['b']=2ks 自动更新(不是拍照片、是活的引用)。好处:① 省内存(不复制)、② 总是最新。坑:遍历时改字典大小 → RuntimeError——for k in d.keys(): d[k*2]=0del d[k] 会抛 RuntimeError: dictionary changed size during iteration(视图动态、遍历中字典结构变了迭代器失效)。解决先固化成列表(快照) for k in list(d.keys())(遍历快照、改的是 d)、或先收集要删的再统一删。改值(不改大小)是允许的for k in d: d[k] = d[k]*2(只改值大小不变 OK)。理解「视图动态(跟字典变):好处省内存/最新;坑遍历中改字典大小抛 RuntimeError(迭代器失效);解决先 list()固化快照;改值不改大小允许」,就掌握了动态性。

三、视图的集合运算

理解 keys/items 视图的集合运算:

keys 视图和 items 视图支持"集合运算"(像 set):
  (values 视图不支持,因为值可能重复/不可哈希)

集合运算符:
  d1.keys() & d2.keys()    # 交集:公共键
  d1.keys() | d2.keys()    # 并集:所有键
  d1.keys() - d2.keys()    # 差集:d1 有 d2 没有的键
  d1.keys() ^ d2.keys()    # 对称差:只在一个里的键

实用场景:
  ① 两字典的公共键:
     common = d1.keys() & d2.keys()
  ② d1 独有的键(d2 里没有的):
     only_d1 = d1.keys() - d2.keys()
  ③ 找出被改动的键(配合值比较):
     changed = {k for k in d1.keys() & d2.keys() if d1[k] != d2[k]}
  ④ 合并所有键:
     all_keys = d1.keys() | d2.keys()

items 视图的集合运算(值需可哈希):
  d1.items() & d2.items()   # 键和值都相同的项
  → 找两个字典"完全相同的键值对"
  ⚠️ 若某个值不可哈希(如 list),items 集合运算会报错

对比手写:
  common = [k for k in d1 if k in d2]     # 手写
  common = d1.keys() & d2.keys()          # 视图(更简洁)

返回的是 set:
  d1.keys() & d2.keys()  → 普通 set(不是视图)

所以 keys/items 视图支持集合运算(&|-^),一行求公共键/独有键/相同项

keys 视图和 items 视图支持「集合运算」(像 set)(values 视图不支持、因值可能重复/不可哈希)。集合运算符d1.keys() & d2.keys()(交集:公共键)、|(并集:所有键)、-(差集:d1 有 d2 没有的键)、^(对称差:只在一个里的键)。实用场景① 两字典的公共键、② d1 独有的键、③ 找被改动的键({k for k in d1.keys() & d2.keys() if d1[k] != d2[k]})、④ 合并所有键items 视图的集合运算(值需可哈希)d1.items() & d2.items() 找完全相同的键值对(若某个值不可哈希如 list 会报错)。对比手写:d1.keys() & d2.keys()[k for k in d1 if k in d2] 更简洁。返回的是 set(不是视图)。理解「keys/items 视图支持集合运算(&交/|并/-差/^对称差)、values 不支持;一行求公共键 d1.keys()&d2.keys()/独有键-/相同项 items()&;items 集合运算要值可哈希」,就掌握了集合运算。

四、字典合并的几种方式

理解字典合并的各种方法:

字典合并方式(冲突时"后者覆盖前者"):

① {**d1, **d2}(解包合并,Python 3.5+):
   merged = {**d1, **d2}
   → 返回新字典,d2 的键覆盖 d1 的同名键
   → 可合并多个:{**d1, **d2, **d3}
   → 可加临时键:{**d1, 'extra': 1}

② d1 | d2(合并运算符,Python 3.9+):
   merged = d1 | d2
   → 返回新字典,语义同解包,更简洁
   → d1 |= d2:原地更新 d1(等价 d1.update(d2))

③ d1.update(d2)(原地更新,最老):
   d1.update(d2)      # 改的是 d1(无返回值)
   → 把 d2 的键值加入 d1,同名覆盖
   → 也可 update(可迭代的键值对)、update(k=v)

对比:
  {**d1,**d2}、d1|d2  → 返回新字典(d1、d2 不变)
  d1|=d2、d1.update(d2) → 原地改 d1

覆盖规则(都一样):
  同名键,后面的值覆盖前面的
  {**{'a':1}, **{'a':2}}  → {'a':2}
  d1={'a':1}; d1|{'a':2}  → {'a':2}

嵌套字典的坑(浅合并):
  这些都是"浅合并"——只合并顶层键
  {**{'x':{'a':1}}, **{'x':{'b':2}}}
  → {'x':{'b':2}}  # x 整个被覆盖,不是深度合并!
  → 深度合并要递归或用第三方库(如 mergedeep)

选择:
  要新字典、清晰 → d1 | d2(3.9+)或 {**d1,**d2}
  原地更新 → d1.update(d2) 或 d1 |= d2

所以合并:{**d1,**d2}/d1|d2(新字典)、update/|=(原地),后者覆盖前者,浅合并

字典合并方式(冲突时「后者覆盖前者」){**d1, **d2}(解包合并,3.5+)——返回新字典、d2 覆盖 d1 同名键、可合并多个、可加临时键;d1 | d2(合并运算符,3.9+)——返回新字典、语义同解包更简洁、d1 |= d2 原地更新;d1.update(d2)(原地更新,最老)——改 d1、同名覆盖。对比:{**d1,**d2}/d1|d2 返回新字典、d1|=d2/update 原地改 d1。覆盖规则:同名键后面的值覆盖前面的。嵌套字典的坑(浅合并):这些都是浅合并、只合并顶层键({**{'x':{'a':1}}, **{'x':{'b':2}}}{'x':{'b':2}} x 整个被覆盖不是深度合并)、深度合并要递归或第三方库。选择:要新字典用 d1 | d2(3.9+)或 {**d1,**d2}、原地更新用 update|=。理解「合并:{**d1,**d2}/d1|d2 返回新字典、update/|=原地改;后者覆盖前者;都是浅合并(嵌套字典顶层键整个覆盖不是深度合并)」,就掌握了字典合并。

五、遍历字典的正确姿势

理解字典遍历的常见写法:

遍历字典的几种方式:

① 遍历键(默认):
   for k in d:              # 直接遍历字典 = 遍历键
   for k in d.keys():      # 等价(更显式)
   → 两者一样,for k in d 更常用

② 遍历值:
   for v in d.values():

③ 遍历键值对(最常用):
   for k, v in d.items():  # 同时拿键和值
   → items() 自动解包成 k, v

④ 带索引遍历:
   for i, (k, v) in enumerate(d.items()):

⑤ 排序遍历:
   for k in sorted(d):                    # 按键排序
   for k, v in sorted(d.items()):         # 按键
   for k, v in sorted(d.items(), key=lambda x: x[1]):  # 按值

性能提示:
  for k in d: v = d[k]      # ✗ 每次索引查找一次
  for k, v in d.items():    # ✓ 一次拿到,更快
  → 要键和值就用 items(),别 for k in d 再 d[k]

遍历中修改的规则(再强调):
  改值(不变大小):for k in d: d[k] = ...   ✓
  改大小(增删键):for k in list(d): ...     ✓(先固化)
                   for k in d: del d[k]      ✗ RuntimeError

删除并遍历的惯用法:
  d = {k: v for k, v in d.items() if keep(k, v)}  # 推导式重建(干净)

所以遍历:for k in d(键)/d.values()/d.items()(键值);要键值用 items 更快;改大小先 list

遍历字典的几种方式① 遍历键(默认) for k in d(= 遍历键,等价 for k in d.keys());② 遍历值 for v in d.values()③ 遍历键值对(最常用) for k, v in d.items()(自动解包);④ 带索引 for i, (k, v) in enumerate(d.items())⑤ 排序遍历 for k in sorted(d)(按键)、sorted(d.items(), key=lambda x: x[1])(按值)。性能提示for k in d: v = d[k](每次索引查找)不如 for k, v in d.items()(一次拿到更快)。遍历中修改的规则:改值(不变大小)OK、改大小先固化 for k in list(d)for k in d: del d[k] 报错。删除并遍历的惯用法d = {k: v for k, v in d.items() if keep(k, v)}(推导式重建)。理解「遍历:for k in d(键)/values()/items()(键值最常用);要键值用 items 更快别 for k in d 再 d[k];改大小先 list、删除用推导式重建」,就掌握了遍历姿势。

六、总结与实践

总结视图和合并:

视图对象速查:
  d.keys()/values()/items() → 动态视图(不是列表)
  特性:动态(跟字典变)、轻量(不复制)
  能:迭代、len、in、集合运算(keys/items)、转 list
  不能:索引、修改
  坑:遍历中改字典大小 → RuntimeError(先 list 固化)

集合运算(keys/items):
  &(公共)| (全部)-(差集)^(对称差)
  d1.keys() & d2.keys()  # 一行求公共键

字典合并:
  {**d1, **d2}      # 新字典(3.5+)
  d1 | d2           # 新字典(3.9+,推荐)
  d1 |= d2 / d1.update(d2)  # 原地
  规则:后者覆盖前者、浅合并(嵌套字典顶层覆盖)

遍历:
  for k in d          # 键
  for k, v in d.items()  # 键值(最常用、最快)

核心总结:
  keys/values/items 返回动态视图(省内存、跟字典变、可集合运算)
  合并用 {**d1,**d2} 或 d1|d2(3.9+),后者覆盖前者
  遍历改大小要先 list()(避免 RuntimeError)

所以视图动态省内存+集合运算,合并用{**}或|(后覆盖前),遍历改大小先 list

视图对象速查d.keys()/values()/items() → 动态视图(不是列表),特性动态(跟字典变)+轻量(不复制),能迭代/len/in/集合运算/转 list、不能索引/修改,坑是遍历中改字典大小 RuntimeError(先 list 固化)。集合运算(keys/items)&(公共)|(全部)-(差集)^(对称差)。字典合并{**d1,**d2}(3.5+)、d1|d2(3.9+推荐)、d1|=d2/update(原地),后者覆盖前者、浅合并。遍历for k in d(键)、for k, v in d.items()(键值最常用最快)。理解「视图动态省内存+可集合运算、不能索引、遍历改大小先 list;合并{**}或|(3.9+)后覆盖前浅合并;遍历用 items 最常用」,就掌握了总结与实践。

记忆钩子:**「dict.keys()/values()/items()返回的不是列表而是『视图对象』(dict_keys/dict_values/dict_items)——①动态(live):字典变化视图自动跟着变(是活的引用不是快照,ks=d.keys()后 d[‘c’]=3 则 ks 里自动出现 c)②轻量:几乎不占内存(不复制数据、只是指向字典的窗口);★Py2 返回列表、Py3 改成视图是迁移坑(需要列表就 list(d.keys()));能做:迭代/len/in/集合运算/转 list,不能索引(d.keys()[0]报错)也不能改;★keys 和 items 视图支持集合运算(&交集=公共键、|并集、-差集=独有键、^对称差),values 不支持;d1.keys()&d2.keys()一行求两字典公共键;★动态性的坑:遍历时增删键(改字典大小)会抛 RuntimeError:dictionary changed size during iteration(迭代器失效),要先固化 for k in list(d.keys())(改值不改大小则允许);字典合并:{**d1,d2}(3.5+)或 d1|d2(3.9+)返回新字典、d1|=d2 或 d1.update(d2)原地更新,冲突时后者覆盖前者,都是浅合并(嵌套字典顶层键整个被覆盖不是深度合并);遍历要键值用 for k,v in d.items()(比 for k in d 再 d[k]快)」

七、常见误区与追问

  • 误区:d.keys() 返回一个键的列表。 返回的是「视图对象」(dict_keys)不是列表——它是字典的动态窗口(字典变化时视图自动跟着变)、几乎不占内存(不复制数据);这是 Python 3 的行为(Python 2 才返回列表快照);需要真正的列表(比如要索引 [0]、要排序后固定)时要显式 list(d.keys())
  • 误区:把 keys() 存下来后字典再改,keys() 还是旧的。 不是——视图是「动态/活的」引用而不是快照:ks = d.keys() 之后往 d 增删键,ks 会自动反映这些变化(d['c']=3ks 里就有了 c);如果你需要「当时的快照」,要 ks = list(d.keys()) 复制一份;这个动态性省内存但也带来「遍历时不能改字典大小」的限制。
  • 误区:遍历字典时可以随意增删键。 不行——因为视图是动态的,遍历过程中增删键(改变字典大小)会导致迭代器失效、抛 RuntimeError: dictionary changed size during iteration;要边遍历边删,先把键固化成列表 for k in list(d.keys()): ... 或用字典推导式重建 d = {k:v for k,v in d.items() if keep(k)};注意「只改值不改大小」(for k in d: d[k] = ...)是允许的。
  • **误区:{**d1, d2} 和 d1 | d2 是深度合并嵌套字典。 都是浅合并——只合并顶层的键,如果某个键的值本身是字典,冲突时是整个替换而不是递归合并:{**{'x':{'a':1}}, **{'x':{'b':2}}} 得到 {'x':{'b':2}}(x 的值被整个覆盖成 {'b':2},不是 {'a':1,'b':2});需要深度合并(递归合并嵌套字典)要自己写递归函数或用第三方库(如 mergedeep)。
  • 追问:dict 的视图对象有哪些独特能力,为什么设计成视图而不是列表? 独特能力:① 动态——总是反映字典的当前状态,不用重新取;② 轻量——不复制数据、几乎零内存开销;③ keys 和 items 视图支持集合运算(& 交集求公共键、| 并集、- 差集求独有键、^ 对称差),可以一行完成两个字典键的比较;设计成视图(而非 Python 2 的列表)主要为了省内存和保持最新——大字典调用 keys() 不必复制一份键列表出来,且视图随字典自动更新;代价是不能像列表那样索引(要先 list())、遍历时不能改字典大小。
  • 追问:合并两个字典有哪几种方式,冲突时谁覆盖谁? 主要三种:① {**d1, **d2}(字典解包,3.5+,返回新字典、d1、d2 不变,可合并多个 {**d1,**d2,**d3}、可加临时键);② d1 | d2(合并运算符,3.9+,返回新字典、语义同解包但更清晰)以及 d1 |= d2(原地更新 d1);③ d1.update(d2)(原地更新 d1、无返回值、最兼容老版本);冲突规则都一样——「后面的覆盖前面的」,即同名键取靠后那个字典的值({**{'a':1}, **{'a':2}}{'a':2});注意都是浅合并;选择:要新字典优先 d1 | d2(3.9+)或 {**d1,**d2},要原地改用 update|=
  • 追问:怎么用视图的集合运算找出两个字典的公共键、各自独有的键、以及值发生变化的键? 公共键(都有的键):d1.keys() & d2.keys();d1 独有(在 d1 不在 d2):d1.keys() - d2.keys();d2 独有:d2.keys() - d1.keys();只在其中一个里的键(对称差):d1.keys() ^ d2.keys();所有键:d1.keys() | d2.keys();值发生变化的键(在两个字典都有但值不同):结合公共键和值比较 {k for k in d1.keys() & d2.keys() if d1[k] != d2[k]};这些都比手写循环判断简洁,返回的是普通 set;注意 values 视图不支持集合运算(值可能重复或不可哈希),items 视图支持但要求所有值可哈希。

八、加强记忆

dict.keys()/values()/items() 返回的不是列表而是「视图对象」(dict_keys/dict_values/dict_items)① 动态(live)——字典变化时视图自动跟着变(是活的引用不是快照,ks = d.keys()d['c']=3 则 ks 里自动出现 c);② 轻量——几乎不占内存(不复制数据、只是指向字典的窗口)。Py2 返回列表、Py3 改成视图是迁移坑(需要列表就 list(d.keys()))。能迭代/len/in/集合运算/转 list,不能索引(d.keys()[0] 报错)也不能改。keys 和 items 视图支持集合运算& 交集=公共键、| 并集、- 差集=独有键、^ 对称差),values 不支持——d1.keys() & d2.keys() 一行求公共键。动态性的坑:遍历时增删键(改字典大小)会抛 RuntimeError: dictionary changed size during iteration,要先固化 for k in list(d.keys())(改值不改大小则允许)。字典合并{**d1, **d2}(3.5+)或 d1 | d2(3.9+)返回新字典、d1 |= d2d1.update(d2) 原地更新,冲突时后者覆盖前者、都是浅合并(嵌套字典顶层键整个被覆盖)。一句话「**keys/values/items 返回动态视图(跟字典变、省内存、不是列表);keys/items 支持集合运算(&公共键/-独有键);遍历中改字典大小抛 RuntimeError(先 list 固化);合并用{**d1,d2}或 d1|d2(3.9+)后者覆盖前者浅合并、update/|=原地」。