Set literal给出了set函数调用的不同结果

时间:2016-10-24 19:29:13

标签: python hash set python-2.x python-internals

为什么conda install -c conda-forge tensorflow函数调用会消除欺骗,但解析集合文字不会?

set

(Python 2.7.12。可能与this类似问题的根本原因相同)

2 个答案:

答案 0 :(得分:55)

设置相等的测试,直到有新的Python版本,他们这样做的顺序可能会根据你将值交给正在构建的集合的形式而有所不同,如下所示。

由于0 == x为真并且 0 == y为真,但x == y false ,此处的行为实际上是 undefined ,因为如果前两个测试也是真的,该集合假定x == y必须为真。

如果您反向传递给set()的列表,那么您将获得与使用文字相同的输出,因为相等测试的顺序发生了变化:

>>> set([y, x, 0])
set([0j, Decimal('0')])

和翻转文字相同:

>>> {y, x, 0}
set([0])

正在发生的事情是set literal 将值加载到堆栈中,然后堆栈值以相反的顺序添加到新的set对象中。

只要先加载0 ,就会对集合中已有的0对其他两个对象进行测试。在首先加载其他两个对象之一的时刻,相等测试失败并且您添加了两个对象:

>>> {y, 0, x}
set([Decimal('0'), 0j])
>>> {x, 0, y}
set([0j, Decimal('0')])

设置文字反向添加元素是支持语法的所有Python版本中存在的错误,一直到Python 2.7.12和3.5.2。它最近已修复,见issue 26020(2.7.13,3.5.3和3.6的一部分,尚未发布)。如果你看2.7.12,你可以看到BUILD_SET in 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 version使用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);

平等测试问题与另一个问题具有相同的根本原因;这里Decimal()类与complex存在一些平等问题,这在Python 3.2中已经修复(通过制作Decimal() support comparisons to complex and a few other numeric types it didn't support before)。

答案 1 :(得分:7)

这完全取决于构造集合的顺序,结合您使用your other question发现的错误。似乎文字的构造方式与从列表中转换的顺序相反。

>>> {0, x, y}
set([0j, Decimal('0')])
>>> {y, x, 0}
set([0])