如果字典键相同,为什么Python只保留最后一个键值对?

时间:2018-08-21 14:53:48

标签: python python-3.x dictionary

假设我创建了一个字典a_dictionary,其中两个键值对具有相同的键:

In [1]: a_dictionary = {'key': 5, 'another_key': 10, 'key': 50} 

In [2]: a_dictionary
Out[2]: {'key': 50, 'another_key': 10}

为什么Python在这里选择保留最后一个键/值对,而不是抛出有关使用相同键的错误(或至少发出警告)?

我认为,主要缺点是您可能会丢失数据而不自觉。

(如果相关,我在Python 3.6.4上运行了上面的代码。)

4 个答案:

答案 0 :(得分:4)

如果您的问题是为什么Python dict显示器最初是用这种方式设计的……可能没人知道。


我们知道何时做出决定。 Python 0.9.x(1991-1993)没有字典显示; Python 1.0.x(1994)做到了。他们的工作与今天完全一样。从文档: 1

  

词典显示将产生一个新的词典对象。

     

从左至右评估键/基准对,以定义   字典的条目:每个键对象都用作键   字典以存储相应的基准。

     

前面列出了对键值类型的限制   部分类型。

     

未检测到重复键之间的冲突;最后   给定键存储的数据(文本中在屏幕上最右边)   价值至上。

然后,对其进行测试:

$ ./python
Python 1.0.1 (Aug 21 2018)
Copyright 1991-1994 Stichting Mathematisch Centrum, Amsterdam
>>> {'key': 1, 'other': 2, 'key': 3}
{'other': 2, 'key': 3}

但是没有提及Guido为什么选择以下设计:

另外,如果您查看具有相似功能的不同语言,其中有些保留像Python这样的最后一个键值对,有些保留任意的键值对,有些则引发某种错误……每种语言都有足够的余地您不能说这是一个显而易见的设计,这就是Guido选择它的原因。


如果您想进行疯狂的猜测,可能并不比您自己想像的要好,那就是我的:

通过创建一个空的dict并将键值对插入其中,编译器不仅可以可以,而且可以可以有效地从文字构造常量值。因此,默认情况下,您会获得重复项允许的,最后键赢的语义;如果您还需要其他任何内容,则必须编写额外的代码。而且,由于没有明显的理由互相挑剔,Guido选择不编写额外的代码。


因此,如果没有令人信服的理由 的设计,为什么在过去的24年中没有人尝试更改它?

好吧,有人提出了功能请求(b.p.o. #16385),以使重复键成为3.4中的错误。 但是显然在建议时将其提上-ideas就消失了。)它可能已经出现了其他几次,但是显然没人希望它进行足够大的改动以推动它。

与此同时,他最接近Python的现有行为的实际论点是Terry J. Reedy的评论:

  

没有更多的用例和支持(来自关于python-ideas的讨论),我认为这应该被拒绝。能够重写键是Python字典的基础,也是为什么它们可用于Python的可变名称空间的基础。一次写一次或一次写密钥一次的字典就不一样了。

     

对于文字,代码生成器可能依赖于能够编写重复键而不必返回并擦除先前的输出。


1。我不认为1.0的文档可以在任何地方直接链接,但是您可以下载the whole 1.0.1 source archive并从TeX源构建文档。

答案 1 :(得分:2)

我认为@tobias_k是最终的答案-否则将会存在不一致之处。如果

{'key': 0, 'key': 1}

抛出一个错误,然后我期望

lst = [('key', 0), ('key', 1)]
dict(lst)

失败,然后我期望

d = {}
d['key'] = 0
d['key'] = 1

也。但是,当然,最后一个选择显然不是我想要的,因此回到链上,我们可以实现当前的行为。

答案 2 :(得分:2)

从概念上讲,您可以将字典创建视为一个迭代的增量过程。换句话说,字典文字的分配:

a_dictionary = {'key': 5, 'another_key': 10, 'key': 50}

等效于一系列单个赋值语句:

a_dictionary['key'] = 5
a_dictionary['another_key'] = 10
a_dictionary['key'] = 50

很自然地,如果一个键多次出现,那么给它重新分配一个新值就没错。

答案 3 :(得分:-2)

通常,您要覆盖值而不是抛出错误。 如果要使用一个字典来保护自己免受覆盖值的影响,请创建一个新的类来包装Dictionary类,并在覆盖任何值时引发错误。