It's a bit strange to need two different-by-identity but equal-by-value tuples, but I've had legitimate reasons to do similarly strange things on occasion, so I think the question deserves an answer at face value.
The reason that y
gives a list of three references to the same tuple is that the tuple (None, None)
is a compile-time constant, so the bytecode for it is a simple LOAD_CONST
which is done inside the list comprehension, and of course loading the same constant three times just creates three references to it, not three copies.
To get around this, we need an expression whose value is (None, None)
but for which the expression is not a compile-time constant. A function call to tuple
does it, at least in the versions of Python I tested (3.5.2 and 3.8.1):
[tuple([None, None]) for _ in range(3)]
Frankly I'm a little surprised that x
does have three different copies, and that is almost certainly an implementation-specific detail that you shouldn't rely on. But likewise, tuple.__new__
does not always create a new tuple; for example tuple( (None, None) )
returns a reference to the actual argument, not a copy of it. So it is not guaranteed that tuple([None, None])
will continue to produce non-equal-by-reference tuples in other versions of Python.