1

I am curious to know the internals of these examples below:

>>> x = []
>>> x.append(x)
>>> print(x)
[[...]]
>>> y = [1]
>>> y.append(y)
>>> print(y)
[1, [...]]
>>> len(x)
1
>>> len(y)
2
>>> x[0]
[[...]]
>>> x[0][0][0] == x
True
>>> y[0]
1
>>> y[1]
[1, [...]]

Demo link

  • How is an infinitely nested one element list being created internally for x ?
DhruvPathak
  • 42,059
  • 16
  • 116
  • 175
  • 2
    You are appending an object reference into the object itself, also creating an infinite list because of that. Which part are you confused about in particular? Do some `print(id(...))`s in a function that recursively goes through your list and you will see what is happening. – Error - Syntactical Remorse Aug 09 '19 at 12:50
  • 2
    If the question was about how does Python "handle" lists that contain themselves, then there is nothing special about it, every list element is a reference to an object, so the list is just holding a reference to itself. But if the question is about how does Python "detect" this situation for example for printing, it should be reopened. – jdehesa Aug 09 '19 at 12:58
  • 1
    @jdehesa to be fare though it is pretty easy to detect cyclical references when the list `print`s because it can just do an `id` check on the element:s `if id(element) == id(self): print('[...]')` as it goes through and prints the `list` inside the list's `__str__` method. – Error - Syntactical Remorse Aug 09 '19 at 13:01
  • 1
    @Error-SyntacticalRemorse For lists it is done at C level on `repr`, using [`Py_ReprEnter`](https://docs.python.org/3/c-api/exceptions.html#c.Py_ReprEnter) and [`Py_ReprLeave`](https://docs.python.org/3/c-api/exceptions.html#c.Py_ReprLeave) functions ([source](https://github.com/python/cpython/blob/v3.7.4/Objects/object.c#L2051-L2124)) You can see it use in the [source of the list `repr` function](https://github.com/python/cpython/blob/v3.7.4/Objects/listobject.c#L335-L389). – jdehesa Aug 09 '19 at 13:06
  • @jdehesa to be fare thats what CPython uses (not Jython nor PyPy). My example was more PyPy to help people who only really know python understand it more. – Error - Syntactical Remorse Aug 09 '19 at 13:09
  • @Error-SyntacticalRemorse Right, although it is a bit more complicated, because the list could be nested several levels deep, it could contain a sublist which contains itself, etc. So you need something like a thread-local collection of objects being repr'd to stop whenever you get to an object that is already there (which is what those functions do). – jdehesa Aug 09 '19 at 13:13

1 Answers1

0

Maybe a similar example in C will help explain?

#include <stdio.h>

typedef struct _node {
    struct _node *next;
    int value;
} node;

int main(void) {
  node a_node;
  a_node.next = &a_node;
  a_node.value = 1;
  node *curr = &a_node;
  for (int index = 0; index < 3; index ++) {
    printf("curr = %p, curr->value = %d\n", curr, curr->value);
    curr = curr->next;
  }
  return 0;
}

Output

$ ./main
curr = 0x7fff3f26d5c8, curr->value = 1
curr = 0x7fff3f26d5c8, curr->value = 1
curr = 0x7fff3f26d5c8, curr->value = 1

You can experiment with the repli.it. Try increasing 3 to a very large number.

Add something like this to your repl.it to get something similar in python:

z = x
for count in range(10):
  print(id(z))
  z = z[0]