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:我可能读文档太迂腐了,但是集合显示的规范说元素将按从左到右的顺序进行计算,但是对于这些被计算元素添加到集合中的顺序是不明确的。有序集合(如列表显示)的插入顺序是显式定义的(出于明显的原因)。我认为之前的行为不是直观的,而不是语言规范的矛盾,但是,正如我所说的,考虑到其他人对我的解读,我的解读可能是不合理的。