This question already has an answer here:
- Dict/Set Parsing Order Consistency 1 answer
Someone asked here why when putting 1
and True
in a set
only 1
is kept.
This is of course because 1==True
. But in which cases 1
is kept and in which cases True
is kept?
Let's see:
passing a list
to build the set
instead of using the set
notation:
>>> set([True,1])
{True}
>>> set([1,True])
{1}
seems logical: set
iterates on the inner list, and doesn't add the second element because it is equal to the first element (note that set([True,1])
cannot yield 1
, because set
cannot know what's inside the list. It may even not be a list
but an iterable)
Now using set
notation:
>>> {True,1}
{1}
>>> {1,True}
{True}
It seems that in that case, the list of items is processed in reverse order (tested on Python 2.7 and Python 3.4).
But is that guaranteed? Or just an implementation detail?
From one of the most recent version, the
dict
preserves order as a side effect of an implementation detail. In 3.7 this behaviour may be guaranteed. Maybe it had also some effect on a set literal.Python 3.6.2:
The order the elements in the set literal will be inserted does not seem to be guaranteed by the language specification. However, Python 3.6 was changed so that it has the expected left-to-right evaluation order. For full details of this change, here is the issue, and also the commit that introduced the change in insertion order.
To describe the change in a bit more detail, building the set literal
{True, 1}
triggers theBUILD_SET
opcode (with oparg equal to 2) after first pushing pointers toTrue
and1
onto the virtual machine's internal stack.In Python 3.4,
BUILD_SET
uses the following loop to insert elements into the set (note that oparg is 2 in our case):Since
1
was added to the stack last, it is popped off first and is the first object inserted into the set.In newer versions of Python (such as 3.6), the
BUILD_SET
opcode usesPEEK
instead ofPOP
:PEEK(i)
fetches the ith item down the stack, so for{True, 1}
, the objectTrue
is added to the set first.Left to right order in the set display is guaranteed by the documentation:
Example:
Therefore,
{1, True}
is effectively:The set may contain only one of
True
or1
because they are duplicates from theset
point of view:It is guaranteed on Python 3 that
1 == True
. See Is `False == 0 and True == 1 in Python an implementation detail or is it guaranteed by the language?:If
{1, True}
prints{True}
then it is a bug ("set_display evaluation order doesn't match documented behaviour"). The output should be the same asset([1, True])
. It works as expected ({1}
) on the recent pypy, jython, and cpython 2.7.13+, 3.5.3+ versions.