有人问here为什么将1
和True
放在仅set
1
中的原因。
这当然是因为1==True
。但是在哪种情况下会保留1
并保留True
的情况?
让我们看看:
传递list
以构建set
而不是使用set
表示法:
>>> set([True,1])
{True}
>>> set([1,True])
{1}
似乎是合乎逻辑的:set
在内部列表上进行迭代,并且不添加第二个元素,因为它等于第一个元素(注意set([True,1])
不能屈服1
,因为set
无法知道列表中的内容。它甚至可能不是list
而是可迭代的)
现在使用set
表示法:
>>> {True,1}
{1}
>>> {1,True}
{True}
在这种情况下,似乎是以相反的顺序处理项目列表(在Python 2.7和Python 3.4上测试)。
但这有保证吗?或者只是一个实现细节?
答案 0 :(得分:5)
语言规范似乎无法保证插入集合文字中元素的顺序。但是,Python 3.6已更改,因此它具有预期的从左到右的评估顺序。有关此更改的完整详细信息,请参阅issue以及引入广告订单更改的commit。
为了更详细地描述更改,在首次推送指向{True, 1}
和{{的指针后,构建集合文字BUILD_SET
会触发True
操作码(oparg等于2) 1}}到虚拟机的内部堆栈上。
在Python 3.4中,BUILD_SET
使用以下循环将元素插入集合中(请注意,在我们的例子中oparg为2):
1
由于while (--oparg >= 0) {
PyObject *item = POP();
if (err == 0)
err = PySet_Add(set, item);
Py_DECREF(item);
最后被添加到堆栈中,它首先被弹出,并且是插入到集合中的第一个对象。
在较新版本的Python(例如3.6)中,BUILD_SET
操作码使用1
代替PEEK
:
POP
PEEK(i)
从堆栈中取出i th 项,因此对于for (i = oparg; i > 0; i--) {
PyObject *item = PEEK(i);
if (err == 0)
err = PySet_Add(set, item);
Py_DECREF(item);
,对象{True, 1}
将首先添加到集合中。
答案 1 :(得分:1)
设置显示中的从左到右顺序由the documentation保证:
从左到右评估其元素并将其添加到设置对象
示例:
>>> 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
中的一个,因为它们与set
观点重复:
>>> hash(1) == hash(True)
True
>>> 1 == True
True
在Python 3上保证1 == True
。见Is `False == 0 and True == 1 in Python an implementation detail or is it guaranteed by the language?:
布尔值:这些表示真值False和True [...]布尔值在几乎所有上下文中的行为分别类似于值0和1,例外的是当转换为字符串时,字符串“False”或者分别返回“True”。
如果{1, True}
打印{True}
,那么它是bug ("set_display evaluation order doesn't match documented behaviour")。输出应与set([1, True])
相同。它在最近的pypy,jython和cpython 2.7.13 +,3.5.3 +版本上按预期工作({1}
。
答案 2 :(得分:0)
从最新版本之一,dict
保留顺序作为实现细节的副作用。在3.7中,可以保证这种行为。 也许它对集合文字也有一些影响。
Python 3.6.2:
>>> {True,1}
{True}
>>> {1,True}
{1}
>>> set([True,1])
{True}
>>> set([1,True])
{1}