1
>>> x = [(None, None), (None, None), (None, None)]
>>> y = [(None, None) for _ in range(3)]
>>> x[0] is x[1]
False
>>> y[0] is y[1]
True

I need a list of n values (None, None) that are not references to same value.

How do I make y behave like x, i.e., to not reference same value?

From this post I expected y to behave like x...

This is using Python 3.6

David Greydanus
  • 2,551
  • 1
  • 23
  • 42
user2052436
  • 4,321
  • 1
  • 25
  • 46
  • >>> x[0] is x[1] True >>> y[0] is y[1] True >>> ................ I cannot replicate your result, what python version are you using? maybe that would work – Knl_Kolhe Feb 02 '20 at 00:45
  • 2
    Tuples are immutable, so it doesn't really matter. – chepner Feb 02 '20 at 00:45
  • 2
    "I need a list of n values `(None, None)` that are not references to same value." - why? It is very rare for that to actually matter. This is a warning sign that you may be misunderstanding something and/or picking a bad design. – user2357112 Feb 02 '20 at 00:48
  • @Knl_Kolhe `Python 3.6.3 :: Intel Corporation` on Linux – user2052436 Feb 02 '20 at 00:54
  • @user2052436 Okay I ran it on Windows. Good to know. – Knl_Kolhe Feb 02 '20 at 00:55
  • 1
    I'm also wondering why you need this... – Kelly Bundy Feb 02 '20 at 01:08
  • @HeapOverflow We later yaml-dump objects that contain such lists. And in case of `y`, dump contains stuff like `- &id001 !!python/tuple [null, null]` and later `- *id001`. The client, who reads yaml, doesn't want it. – user2052436 Feb 02 '20 at 01:48
  • 2
    So that's an [XY problem](http://xyproblem.info/) then? And [this](https://stackoverflow.com/q/13518819/12671057) is your real problem and its answer applies to your case? – Kelly Bundy Feb 02 '20 at 02:36

3 Answers3

4

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.

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • 1
    I'd say it's highly unlikely that `tuple(no_tuple)` will deduplicate. For `tuple(a_tuple)` it's clear how and why it doesn't create a new tuple, and [the docs](https://docs.python.org/3/library/stdtypes.html#typesseq-tuple) also explicitly mention this case: *"If iterable is already a tuple, it is returned unchanged"*. Btw, this is apparently an XY problem after all, see the latest comments under the question. – Kelly Bundy Feb 02 '20 at 17:48
3

You could use the tuple function on a temporary list:

>>> x = [(None, None), (None, None), (None, None)]
>>> y = [tuple([None, None]) for _ in range(3)]
>>> x[0] is x[1]
False
>>> y[0] is y[1]
False
David Greydanus
  • 2,551
  • 1
  • 23
  • 42
0

y = [tuple([None, None]) for _ in range(3)] should work.