设置文字给出了设置函数调用的不同结果
为什么set
函数会调用dupe,但是parsingset literal却不行?
>>> x = Decimal('0') >>> y = complex(0,0) >>> set([0, x, y]) {0} >>> {0, x, y} {Decimal('0'), 0j}
(Python 2.7.12。可能与这个类似的问题相同的根本原因)
设置相等的testing,直到有新的Python发行版为止,它们的执行顺序可能会根据将值传递给正在构造的集合的表单而有所不同,正如我将在下面显示的那样。
由于0 == x
是真的, 0 == y
是真的,但是x == y
是假的 ,这里的行为是不确定的 ,因为如果前两个testing是真的,假设x == y
必须是真的。
如果反转传递给set()
的列表,那么您将得到与使用文字相同的输出,因为相等性testing的顺序会发生变化:
>>> set([y, x, 0]) set([0j, Decimal('0')])
和扭转字面意思一样:
>>> {y, x, 0} set([0])
发生的事情是,设置字面值将值加载到堆栈上,然后堆栈值以相反的顺序添加到新的设置对象。
只要首先加载0
,则另外两个对象然后针对已经在集合中的0
进行testing。 当其他两个对象中的一个首先被加载时,相等性testing失败,并且添加两个对象:
>>> {y, 0, x} set([Decimal('0'), 0j]) >>> {x, 0, y} set([0j, Decimal('0')])
该设置字面值反向添加元素是所有版本的Python中支持该语法的错误,直到Python 2.7.12和3.5.2。 最近修正了,见第26020号 ( 2.7.13,3.5.3和3.6部分,其中没有一个已经发布)。 如果你看2.7.12,你可以看到BUILD_SET
中的ceval.c
读栈:
# oparg is the number of elements to take from the stack to add for (; --oparg >= 0;) { w = POP(); if (err == 0) err = PySet_Add(x, w); Py_DECREF(w); }
而字节码以相反的顺序向堆栈添加元素(首先在堆栈上推0
):
>>> from dis import dis >>> dis(compile('{0, x, y}', '', 'eval')) 2 0 LOAD_CONST 1 (0) 3 LOAD_GLOBAL 0 (x) 6 LOAD_GLOBAL 1 (y) 9 BUILD_SET 3 12 RETURN_VALUE
解决方法是以相反的顺序从堆栈中读取元素; Python 2.7.13版本使用PEEK()
而不是POP()
(和STACKADJ()
来从堆栈中删除元素):
for (i = oparg; i > 0; i--) { w = PEEK(i); if (err == 0) err = PySet_Add(x, w); Py_DECREF(w); } STACKADJ(-oparg);
平等testing问题与其他问题具有相同的根本原因; Decimal()
类在complex
情况下有一些相同的问题,在Python 3.2中已经修复了(通过将Decimal()
支持比较为complex
以及之前不支持的其他一些数字types )。
这完全取决于这个集合的构build顺序,再加上你在其他问题中发现的错误。 看起来,文字是按照与列表转换相反的顺序构造的。
>>> {0, x, y} set([0j, Decimal('0')]) >>> {y, x, 0} set([0])