Python 集合中的插入顺序(在分析{}时)
有人问为什么将Python 集合中的插入顺序(在分析{}时),python,set,python-internals,Python,Set,Python Internals,有人问为什么将1和True放入集合时只保留1 这当然是因为1==True。但在哪些情况下保留1,在哪些情况下保留True 让我们看看: 传递列表以构建集合,而不是使用集合符号: >>> set([True,1]) {True} >>> set([1,True]) {1} >>> {True,1} {1} >>> {1,True} {True} 似乎合乎逻辑:set在内部列表上迭代,并且不添加第二个元素,因为它等于第一个
1
和True
放入集合
时只保留1
这当然是因为1==True
。但在哪些情况下保留1
,在哪些情况下保留True
让我们看看:
传递列表
以构建集合
,而不是使用集合
符号:
>>> set([True,1])
{True}
>>> set([1,True])
{1}
>>> {True,1}
{1}
>>> {1,True}
{True}
似乎合乎逻辑:set
在内部列表上迭代,并且不添加第二个元素,因为它等于第一个元素(注意set([True,1])
不能产生1
,因为set
无法知道列表中有什么。它甚至可能不是列表,而是一个iterable)
现在使用set
符号:
>>> set([True,1])
{True}
>>> set([1,True])
{1}
>>> {True,1}
{1}
>>> {1,True}
{True}
在这种情况下,项目列表的处理顺序似乎是相反的(在Python2.7和Python3.4上进行了测试)
但这能保证吗?或者仅仅是一个实现细节?从最新版本的一个版本来看,dict
保留了作为实现细节副作用的顺序。在3.7中,可以保证这种行为。也许它对集合文字也有一些影响
Python 3.6.2:
>>> {True,1}
{True}
>>> {1,True}
{1}
>>> set([True,1])
{True}
>>> set([1,True])
{1}
语言规范似乎不能保证集合文字中元素的插入顺序。但是,Python3.6进行了更改,使其具有预期的从左到右的求值顺序。有关此更改的完整详细信息,请参见,以及按插入顺序引入此更改的
为了更详细地描述此更改,在将指向True
和1
的指针推到虚拟机的内部堆栈上之后,构建set literal{True,1}
会触发BUILD\u set
操作码(oparg等于2)
在Python 3.4中,使用以下循环将元素插入到集合中(注意,在本例中oparg是2):
而(--oparg>=0){
PyObject*item=POP();
如果(错误==0)
err=PySet\u添加(集合、项目);
Py_DECREF(项目);
由于1
是最后添加到堆栈的,因此它首先弹出,是插入到集合中的第一个对象
在较新版本的Python(如3.6)中,操作码使用PEEK
而不是POP
:
for(i=oparg;i>0;i--){
PyObject*项目=PEEK(i);
如果(错误==0)
err=PySet\u添加(集合、项目);
Py_DECREF(项目);
获取堆栈中的第i项,因此对于{True,1}
,对象True
首先添加到集合中。集合显示中从左到右的顺序由以下各项保证:
其元素从左到右求值并添加到集合对象中
例如:
>>> def f(i, seq="left to right".split()): print(seq[i])
>>> {f(0), f(1), f(2)}
left
to
right
{None}
因此,{1,True}
实际上是:
>>> S = set()
>>> S.add(1)
>>> S.add(True) # no effect according to docs
>>> S
{1}
集合只能包含True
或1
中的一个,因为它们从集合
的角度来看是重复的:
>>> hash(1) == hash(True)
True
>>> 1 == True
True
Python 3保证1==True
。请参阅:
布尔值:它们表示真值False和True[…]布尔值在几乎所有上下文中的行为分别类似于值0和1,例外情况是当转换为字符串时,字符串分别返回“False”或“True”
如果{1,True}
打印{True}
,则输出应与集([1,True])
相同。它在最近的pypy、jython和cpython 2.7.13+、3.5.3+版本上按预期工作({1}
给我{1}{True})
在Python3.6.1上。无论如何,{True}=={1}
的计算结果为True
。或1
。我不知道了。罪魁祸首可能是BUILD\u集
字节码指令,但我找不到它的规范。如果有人想深入了解源代码,我会看看。很高兴看到你跟进了这个问题,Jean,可能值得添加一些python内部或CPython标记s、 只是想一想,当我找到timeok时,我会自己做更多的挖掘,所以它似乎是由实现定义的,不再是这样。有官方链接来证明吗?请注意set([True,1]))
无法生成1
,因为set
无法知道列表中的内容。它甚至可能不是一个列表,而是一个生成器。它是关于字典中的顺序。不是关于集合符号解析的顺序。很抱歉,我无法投票或接受这个答案。但是感谢3.6上的测试。您可能会发现一些问题g这里。相关:极好的答案。我怀疑解析技巧,但挖掘出来是另一回事。正如我预期的那样。所以基本上,答案是“一个实现细节”。顺便说一句,挖掘得不错,+1顺序是。就连你链接的bug标题也如此明确地说:“设置显示评估顺序与记录的行为不匹配”@jfs:我可能读文档太迂腐了,但是集合显示的规范说元素将按从左到右的顺序进行计算,但是对于这些被计算元素添加到集合中的顺序是不明确的。有序集合(如列表显示)的插入顺序是显式定义的(出于明显的原因)。我认为之前的行为不是直观的,而不是语言规范的矛盾,但是,正如我所说的,考虑到其他人对我的解读,我的解读可能是不合理的。