Article Thumbnail

What is Python's Ellipsis Object?

An introduction to Python's ellipsis object

Florian Dahlitz
6 min
July 30, 2021

Introduction

Some time ago, I stumbled across a Python code snippet, which looked like this:

class CustomException(Exception):
    ...

At first, I thought it would just be a sort of pseudo-code. In Python, you usually use the pass keyword to fill a function (or at least something indented), which is not fully implemented yet. My thoughts were somewhat the following: "Ah someone wants to indicate that more code is following, but the code is truncated here, to just get the idea." Consequently, using the pass keyword would mean that something is not yet implemented. At this point, I haven't thought about it much again but used it sometimes on my own to explain a certain concept to colleagues.

A few weeks later, I stumbled upon it again and this time, it was in a running code example. I got curious and wanted to find out, whether this is really a language feature or just a side effect from something else. It turned out that it was the first time I actively identified Python's ellipsis object!

In this article, we will have a closer look at the ellipsis object, where you can use it in your day to day life as a programmer, and which famous Python packages use it.

General Information

Before we jump into possible use cases, let us first find out, what the ellipsis object is. In essence, it is a built-in constant [1] like True and False:

>>> True
True
>>> Ellipsis
Ellipsis
>>> ...
Ellipsis

To be more precise, Ellipsis is the built-in constant and ... (three dots) is the corresponding literal. You can also find it as a defined token in Lib/token.py [2]:

# previous code in token.py
ELLIPSIS = 52
# subsequent code in token.py

The documentation states that the ellipsis object is a "[s]pecial value used mostly in conjunction with extended slicing syntax for user-defined container data types." [1] However, I have not identified a slice use case despite the usage in the third-party package NumPy [3]. To me, it seems to be a "whatever" literal. With that being said, if you have a certain function or class where you want to implement a special feature and look for a non-used literal, the ellipsis object may be right for you. You will see that in a later section.

Type Annotations

Without further research, I pretty soon identified a scenario, where I already used the ellipsis object: Type annotations. To illustrate the concept, let us assume we have a function, which takes zero arguments and returns a tuple:

def return_tuple() -> tuple:
    pass

If you want to give the user of the function more information, you could also specify what the tuple contains. For instance, it could return a tuple consisting of two integers:

def return_tuple() -> tuple[int, int]:
    pass

How would you annotate a tuple of integers, which can have any length greater than zero? Simply use an ellipsis:

def return_tuple() -> tuple[int, ...]:
    pass

But wait, there is more. Suppose you have a function returning a callable. If the signature of the callable, that is being returned, can vary, you can use the ellipsis object to indicate this:

from typing import Callable

def return_callable() -> Callable[..., int]:
    pass

The given code snippet means that a callable with an unknown signature is returned, but the return type of the callable is specified: It is an integer.

Great! At this point, we identified the first scenario, where we can utilise the ellipsis object in our Python code. Let's see how we can benefit from it when implementing classes and functions.

Drop-in Replacement for the Pass-Keyword

In the introductory section, there was already a hint that the ellipsis object could also be used as a drop-in replacement for the pass keyword. Remember, that the pass keyword is used if you want to define a function, class, for-loop or something similar but do not want to implement it yet. Because the Python syntax requires an indentation after these constructs, the pass keyword is used to satisfy Python on the one hand and you on the other hand as you do not need to implement it yet. Here is an example:

def my_function():
    pass

In essence, even the pass keyword is not required. You only need to write down an expression. This could be a number ...

def my_function():
    1

... or the ellipsis literal:

def my_function():
    ...

Personally, I like to use the pass keyword to indicate that something is implemented exactly the way I want it. An example would be creating a custom exception without adding further details:

class CustomException(Exception):
    pass

On the other hand, using the ellipsis object may indicate that a certain function is not yet fully implemented, but raising a NotImplemented exception seems to be overkill. To summarise: Yes, it can be used as a drop-in replacement for the pass keyword, but you probably should not do it, because there is a reason why everyone uses pass. Using it in articles to indicate further code lines should be fine, though!

The Ellipsis Object in the Wild

As stated earlier, if you want to implement a certain feature where you need a non-used literal, you can use the ellipsis object. Typer [4] - a package for implementing command-line interfaces - and FastAPI [5] (web framework) are prominent examples utilising it. In FastAPI it is used to make parameters required [6] and in Typer, it is pretty similar: It indicates that a command-line argument is required [7].

So if you see yourself in the situation that you are developing a new tool or library and need a placeholder literal, remind yourself of the ellipsis object!

Summary

Congratulations, you have made it through the article. In this article, you learnt what the ellipsis object is and where you can use it. Possible use cases are type annotations, drop-in replacement for pass or using it for certain features where you need a non-used literal. Furthermore, we had a look at third-party packages utilising the ellipsis object to realise special features.

I hope you enjoyed reading the article. Make sure to share it with your friends and colleagues. If you have not already, follow me on Twitter, where I am @DahlitzF and subscribe to my newsletter, so you won't miss any future articles. Stay curious and keep coding!

References