Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ellipsis lists [...] and concatenating a list to itself [duplicate]

EDIT: I was careless in my original examples. The behaviour occurs not when I add list A to itself, but rather when I add a list containing list A to A itself. Please see the corrected examples below.


I am trying to understand how ellipsis lists (those lists that appear as [...] and occur when you have a list references itself) works in Python 2.

In particular, I want to know why, if A is a list, A = A + A seems to work differently to A += A (and A.append(A)).

That is, why do you get:

>>> a = [1, 2]  
>>> a = a + [a]
>>> a  
[1, 2, [1, 2]]

vs.

>>> a = [1, 2]  
>>> a += [a]
>>> a
[1, 2, [...]]

(Note that a.append(a) seems to work for me just as the latter did.)

Any additional more general information regarding this ellipsis list phenomenon would also be much appreciated if it helps to clarify things.

like image 899
12qu Avatar asked Mar 20 '12 08:03

12qu


1 Answers

Edit: (to address the additional issues raised by your edits to the question):

a = a + b and a += b are not the same operation. The former executes a.__add__(b), the latter executes a.__iadd__(b) ("in-place add").

The difference between the two is that the former always creates a new object (and rebinds the name a to that new object) while the latter modifies the object in-place (if it can, and with a list, it can).

To illustrate this, just look at the addresses of your objects:

>>> a = [1, 2]
>>> id(a)
34660104
>>> a = a + [a]
>>> id(a)
34657224
>>> id(a[2])
34660104

The "new" a was constructed from scratch, first taking the values from the old list a, then concatenating the reference to the old object to it.

Contrast this to:

>>> a = [1, 2]
>>> id(a)
34658632
>>> a += [a]
>>> id(a)
34658632
>>> id(a[2])
34658632

(Old answer, explaining cyclic references):

Consider this:

>>> a = [1, 2]; a += a
>>> a
[1, 2, 1, 2]
>>> a = [1, 2]; a.extend(a)
>>> a
[1, 2, 1, 2]
>>> a = [1, 2]; a += [a]
>>> a
[1, 2, [...]]
>>> a = [1, 2]; a.append(a)
>>> a
[1, 2, [...]]

So, to summarize the first part:

For lists, a += a is equivalent to calling a.extend(a) which modifies a in-place, adding copies of the elements found in a at the start of this operation.

Conversely, a += [a] corresponds to a.append(a), both of which create a reference to the list a (i. e. a pointer to its address in memory) and add that to the list. Which constitutes a so-called "cyclic reference".

If you were to look at the internal representation of a at that point, it would look something like this:

a:    Reference to a list object at address 0xDEADBEEF
a[0]: Reference to the integer object "1"
a[1]: Reference to the integer object "2"
a[2]: Reference to the same list object at address 0xDEADBEEF

Old Python versions (pre-1.5.1) were not smart enough to detect that, so if you were to do a print a, you'd get [1, 2, [1, 2, [1, 2, [1, 2, [1, 2, [1, 2, ... etc. in an infinite loop. Since Python 1.5.1, the interpreter detects this, printing [1, 2, [...]] instead.

like image 194
Tim Pietzcker Avatar answered Oct 11 '22 11:10

Tim Pietzcker