Search code examples
pythonrecursionellipsisrecursive-datastructures

What does Python mean by printing "[...]" for an object reference?


I'm printing a value of a what I thought was a list, but the output that I get is:

[...]

What does this represent? How do I test for it? I've tried:

myVar.__repr__() != '[...]'

and

myVar.__repr_() != Ellipsis

but no dice...

Here's a cutdown of the code that's giving the issue:

def buildPaths(graph, start, end, path=[], totalPaths=[]):
    """
    returns list of all possible paths from start node to the end node
    """
    path = path + [start]
    if start == end:
        return path
    for nextNode in graph.childrenOf(start):
        if nextNode not in path:
            newPath = buildPaths(graph, nextNode, end, path, totalPaths)
            if newPath != []: # test
                totalPaths.append(newPath)
    return totalPaths

totalPaths contains a LOT of [...] supposedly recursive lists, but I can't see why. I've altered the test at #test to prevent this.

I've also tried:

def buildPaths(graph, thisNode, end, path=[], totalPaths=None):
    """
   returns list of all possible paths from start node to the end node
   """
    path = path + [thisNode]
    if thisNode == end:
        return path
    for nextNode in graph.childrenOf(thisNode):
        if nextNode not in path:
            newPath = buildPaths(graph, nextNode, end, path, totalPaths)
            if newPath != None:
                if totalPaths == None:
                    totalPaths = [newPath]
                else:
                    totalPaths.append(newPath)
    return totalPaths

in order to explicitly return None for empty paths.


Solution

  • Depending on the context here it could different things:

    indexing/slicing with Ellipsis

    I think it's not implemented for any python class but it should represent an arbitary number of data structure nestings (as much needed). So for example: a[..., 1] should return all the second elements of the innermost nested structure:

    >>> import numpy as np
    >>> a = np.arange(27).reshape(3,3,3)  # 3dimensional array
    >>> a[..., 1]  # this returns a slice through the array in the third dimension
    array([[ 1,  4,  7],
           [10, 13, 16],
           [19, 22, 25]])
    >>> a[0, ...]  # This returns a slice through the first dimension
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]])
    

    and to check for this ... you compare it to an Ellipsis (this is a singleton so recommended is using is:

    >>> ... is Ellipsis
    True
    >>> Ellipsis in [...]
    True
    # Another (more or less) equivalent alternative to the previous line:
    >>> any(i is Ellipsis for i in [1, ..., 2]) 
    True
    

    Recursive Datastructures

    The other case in which you see an [...] in your output is if you have the sequence inside the sequence itself. Here it stands for an infinite deeply nested sequence (that's not printable). For example:

    >>> alist = ['a', 'b', 'c']
    >>> alist[0] = alist
    >>> alist
    [[...], 'b', 'c']
    
    # Infinite deeply nested so you can use as many leading [0] as you want
    >>> alist[0][1] 
    'b'
    >>> alist[0][0][0][0][0][1] 
    'b'
    >>> alist[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][1] 
    'b'
    

    You can even replace it several times:

    >>> alist[2] = alist
    >>> alist
    [[...], 'b', [...]]
    >>> alist[1] = alist
    >>> alist
    [[...], [...], [...]]
    

    To test if you have any such recursion in your output you need to check if the data-structure itself is also one of the elements:

    >>> alist in alist
    True
    >>> any(i is alist for i in alist)
    True
    

    Another way to get a more meaningful output is using pprint.pprint:

    >>> import pprint
    >>> pprint.pprint(alist)  # Assuming you only replaced the first element:
    [<Recursion on list with id=1628861250120>, 'b', 'c']