当迭代从dict
子类化的对象的键时,我在Python 3.6.3中遇到了奇怪的行为。我已经理解dict.keys()
的行为从Python 2变为Python 3以返回dict_keys
对象,因此看到的浅拷贝行为是可以理解的。我不明白为什么dict_keys
中的一个元素可以通过显式搜索找到,但在迭代dict_keys
对象时不会出现。
这是MWE:
#!/usr/bin/env python
from __future__ import print_function
class chemdist(dict):
def normalize_names(self, force_static=False):
if force_static:
raw_chems = tuple(self.keys())
else:
raw_chems = self.keys()
print("raw_chems is " + repr(raw_chems))
if 'Ar' in raw_chems:
print("Found Argon!")
for old_chem in raw_chems:
print("Checking {:s}".format(old_chem))
if old_chem.startswith('_'):
new_chem = old_chem
else:
new_chem = '_' + old_chem
if old_chem != new_chem:
curr_keys = self.keys()
if new_chem in curr_keys:
self[new_chem] += self[old_chem]
else:
self[new_chem] = self[old_chem]
del self[old_chem]
def main():
h_dryair = {
'N2' : 78.084,
'O2' : 20.947,
'Ar' : 0.934,
'CO2' : 0.0350
}
print("Test 1: Not static")
cd_dryair = chemdist(h_dryair)
print(" Unnormalized:")
print(cd_dryair)
print("\n Normalizing:")
cd_dryair.normalize_names()
print("\n Normalized:")
print(cd_dryair)
print("\n\nTest 2: Static")
cd_dryair = chemdist(h_dryair)
print(" Unnormalized:")
print(cd_dryair)
print("\n Normalizing:")
cd_dryair.normalize_names(force_static=True)
print("\n Normalized:")
print(cd_dryair)
main()
以下是结果:
Test 1: Not static
Unnormalized:
{'N2': 78.084, 'O2': 20.947, 'Ar': 0.934, 'CO2': 0.035}
Normalizing:
raw_chems is dict_keys(['N2', 'O2', 'Ar', 'CO2'])
Found Argon!
Checking N2
Checking O2
Checking CO2
Checking _N2
Checking _O2
Checking _CO2
Normalized:
{'Ar': 0.934, '_N2': 78.084, '_O2': 20.947, '_CO2': 0.035}
Test 2: Not static
Unnormalized:
{'N2': 78.084, 'O2': 20.947, 'Ar': 0.934, 'CO2': 0.035}
Normalizing:
raw_chems is ('N2', 'O2', 'Ar', 'CO2')
Found Argon!
Checking N2
Checking O2
Checking Ar
Checking CO2
Normalized:
{'_N2': 78.084, '_O2': 20.947, '_Ar': 0.934, '_CO2': 0.035}
在测试1中,通过在'Ar'
中明确搜索元素来找到元素raw_chems
。但是,当迭代raw_chems
时,元素'Ar'
永远不会出现("Checking Ar"
永远不会出现)。
在测试2中,raw_chems
会转换为元组,"Checking Ar"
会按预期显示。
此外,比较最终的规范化对象内容:在测试1中,'Ar'
密钥未被'_Ar'
替换,因为它在测试2中。'Ar'
是唯一的密钥。测试1没有以下划线为前缀。
我对这个问题的原因有两个猜测:dict_keys
有一些显而易见的每个人但是我的行为或一些奇怪的事情正在发生,因为我正在进行子类化dict
。可能是两者的结合。
在有人试图改变我要问的问题之前,我试图解决的Big Picture问题是将dict键重命名为规范形式(HH
,{{ 1}}和H2
都表示相同的事情)。虽然对于某些人来说这可能是一个更有趣的问题,但我要问的是消失的密钥,所以请关注 x 而不是 y 你想象......:)
答案 0 :(得分:0)
在python 3中dict.keys()
是一个关于实际键和更改的视图,如果字典键发生了变化。如果迭代键,则不得更改字典的键。
答案 1 :(得分:0)
dict.keys()
将视图返回到字典的键中。当您更改字典的键时,您也会更改dict.keys()
的内容。这可能会导致项目被跳过,就像在迭代列表时删除项目一样:
假设您即将处理O2
:
N2
--> O2
Ar
CO2
您处理它并删除O2
并添加_O2
。在此过程中,Ar
会滑入包含O2
。
N2
--> Ar
CO2
_O2
现在进入循环的下一次迭代,这将推进"当前项目"指向下一项的指针:
N2
Ar
--> CO2
_O2
您刚刚跳过Ar
。
您可以找到一种解决方案:在迭代密钥之前复制密钥。另一种解决方案是构建新的字典而不是修改现有的字典。我通常推荐这种方法,因为它可以使你的代码在这个过程中更加清洁。
第三种解决方案是以相反的顺序迭代列表,这样当您删除列表时,您仍然需要处理的项目不会移动;但是,字典键不能保证按任何特定顺序排列。虽然它们属于您正在使用的Python版本,但您不应该依赖它。