← 输入框|python.land|刷新

Python Ellipsis (triple dots): What is it, How to Use

Chances are, you never ran into the Python ellipsis, consisting of three consecutive dots (…). I did, and I decided to do some research and find out more about this somewhat mysterious language element. Let’s go and explore together!

What is the Python ellipsis?

An ellipsis is built from three consecutive dots, like this: …

It’s a well-known language construct in many western languages. But it’s also part of the Python programming language! Let’s inspect this thing in the REPL:

>>> ...
Ellipsis
>>> # OK, it's an object (like everything in Python)
>>> type(...)
<class 'ellipsis'>
>>> help(...)

Help on ellipsis object:

class ellipsis(object)
 |  The type of the Ellipsis singleton.
 |
 |  Methods defined here:
 |
 |  __reduce__(self, /)
 |      Helper for pickle.
 |
 |  __repr__(self, /)
 |      Return repr(self).
 |
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |
 |  __new__(*args, **kwargs)
 |      Create and return a new object.  See help(type) for accurate signature.
Code language: Python (python)

There is exactly one instance of the class ellipsis, called Ellipsis, and ... is just another way to write it: ... is Ellipsis gives True. If you ever need the type itself, for example in a type hint, it’s available as types.EllipsisType. That’s all not very helpful, so let’s see what the official documentation has to say about this mysterious thing. Turns out there’s not much there, either:

‘Special value used mostly in conjunction with extended slicing syntax for user-defined container data types.’

Looks like this ain’t gonna be a walk in the park. We’ll need to do more digging! After some searching, I found out the following uses for the Ellipsis object.

Where do we use the Python ellipsis?

Python ellipsis as a placeholder

You can use it as a placeholder, e.g. when you intend to fill in a Python function later on, but still want to have a valid syntax:

def todo():
    ...Code language: Python (python)

Many people use pass in such cases, but this looks nicer (personal taste, of course), and there are no real downsides I can think of.

The ellipsis in type hints and stub files

These days, the place where you’re most likely to see the ellipsis is in type hints. It has a special meaning in a few spots:

  • tuple[int, ...] means a tuple of any length that contains only integers. Without the ellipsis, tuple[int] would mean a tuple with exactly one integer.
  • Callable[..., str] means a function that takes any arguments and returns a string.
  • In a Protocol class, or any method you only declare without implementing, people write ... as the body.

Here’s what that looks like in code:

from collections.abc import Callable
from typing import Protocol


def average(numbers: tuple[int, ...]) -> float:
    return sum(numbers) / len(numbers)


def call_twice(func: Callable[..., str]) -> None:
    print(func())
    print(func())


class Greeter(Protocol):
    def greet(self, name: str) -> str: ...Code language: Python (python)

You’ll also find the ellipsis all over stub files. These are files with a .pyi extension that only contain type information for a module, for example for a library written in C. Every function body and default value in a stub is replaced by ...:

def average(numbers: tuple[int, ...]) -> float: ...
def connect(host: str, port: int = ...) -> None: ...Code language: Python (python)

Type checkers and editors read these files to give you code completion and warnings. The typing module documentation has all the details.

The ellipsis in NumPy slice notation

Another well-known application of the ellipsis is in the NumPy library. With NumPy, you can slice multiple dimensions at once by using commas. To demonstrate, first create a simple three-dimensional matrix:

>>> x = np.array([   [ [1],[2],[3] ], [ [4],[5],[6] ]   ])Code language: Python (python)

Now these two operations are equivalent:

>>> x[:,:]
array([[[1],
        [2],
        [3]],

       [[4],
        [5],
        [6]]])

>>> x[...]
array([[[1],
        [2],
        [3]],

       [[4],
        [5],
        [6]]])Code language: Python (python)

To get all the data elements at index 1 of the first dimension (the second element, since we count from 0):

>>> x[1, ...]
array([[4],
       [5],
       [6]])Code language: Python (python)

The ellipsis stands for “all remaining dimensions”, so it also works at the front. x[..., 0] takes index 0 of the last dimension:

>>> x[..., 0]
array([[1, 2, 3],
       [4, 5, 6]])Code language: Python (python)

You can find a more thorough explanation on this blog and the NumPy docs.

End of queue marker

Some use the ellipsis to mark the end of a queue. It makes sense:

  • You don’t need to create a custom object
  • It’s not something that you usually encounter in queues, so it’s unique enough to be an end-of-queue marker

Circular references

When creating circular references, Python shows an ellipsis in the REPL:

>>> a = [0, 1]
>>> a[0] = a
>>> a
[[...], 1]Code language: Python (python)

This doesn’t mean Python replaces the reference with an ellipsis, it just shows one instead of crashing in an infinite loop and/or filling up your screen. You can test this yourself. You can keep asking for nested objects, and it will still result in the original a:

>>> a = [0, 1]
>>> a[0] = a
>>> a[0][0][0][0][0][0][0][0][0][0]
[[...], 1]Code language: Python (python)

1 thought on “Python Ellipsis (triple dots): What is it, How to Use”

  1. I like … as a replacement for pass because you can put a breakpoint on … but not on pass because pass doesn’t generate any bytecode. It also looks nicer and conveys the thought “to be continued…”

Leave a Comment