Python Pass vs. Ellipsis

📆 ⏱️ 3 min read 📚 published in python and software engineering

I’ve recently had a nerdy discussion with a colleague during a code review about when to use Pythons pass statement1 vs. the ellipsis literal ...2

You’ve probably seen the pass statement in Python code as a means to indicate that a block is intentionally left empty to avoid a SyntaxError. Like in the following except block where we expect that an exception might be raised but just want to ignore it:

1
2
3
4
try:
    client.get(id=42)
except ApiException:
    pass

Somehow the ellipsis literal ... has gotten some hype lately and some people started doing this:

1
2
3
4
try:
    client.get(id=42)
except ApiException:
    ...

While this is syntactically valid Python code the semantics are weird to me.

To me, the pass statement effectively communicates that we should just pass this block without running any code at all. While the ... are more like “there is something to be expected here in the future”. Therefore, the ... can be thought of a placeholder.

Whenever I see the ... my head auto-plays a Dun Dun Dun sound effect - to emphasize the suspension of code 🙊. It would be a pity if it’s a suspension of code until all eternity if you use at as a pass replacement.

Where should I use the ... ?

As I’ve already mentioned you should use the ... as a placeholder where eventually some could should appear, but you don’t necessarily want to raise NotImplementedError.

A similar situation where I use it is with Pythons Abstract Base Classes to indicate that the function body needs to be implemented by a subclass.

1
2
3
4
5
6
from abc import abstractmethod

class AbstractEventLoop:
    @abstractmethod
    def run(self, ...):
        ...

The next place where I use it is in Python stub files to indicate an ignored function body:

1
def get(id: int) -> Model: ...

The fourth place is in extended slicing with custom container types, for what the ... was originally introduced for. For example, the numpy array indexing supports it:

1
2
3
4
from numpy import arange
a = arange(16).reshape(2, 2, 2, 2)

flat_a = a[..., 0].flatten()

But what is the ... literal ?

The ellipsis literal ... is syntactic sugar for the Ellipsis object, which is the sole instance of the types.EllipsisType type. You can use it like every other object in Python, except that you can’t create a new one. Thus, when you override it (yes, that’s possible: __builtins__.Ellipsis = 42) you can’t really get it back 🤷 - thus, don’t do it.