How can I set a placeholder (sentinel) without using None?

5 days ago 6
ARTICLE AD BOX

You should probably use a sentinel value. In Python 3.14 and earlier, this is typically (though not always!) done using a bare object combined with an identity (is) check:

import ast SENTINEL = object() user_input = SENTINEL while user_input is SENTINEL: try: user_input = ast.literal_eval(input("Enter a Python literal: ")) except: pass

The benefit of this pattern is that SENTINEL is completely unique. No other user-created object will ever compare identical to it. As such, there's no chance of collision with user-provided input; even another object() will not be identical to your sentinel. In fact, you can create multiple unique sentinels for different use cases:

SENTINEL_1 = object() SENTINEL_2 = object() assert SENTINEL_1 is not SENTINEL_2

Be careful to always use identity, not equality (==), when comparing to sentinels. Otherwise, someone could redefine __eq__ on the other object in an undesirable way:

SENTINEL = object() class AlwaysEqual: def __eq__(self, other, /): return True assert SENTINEL is not AlwaysEqual() assert SENTINEL == AlwaysEqual() # Bad!

In Python 3.15, using an object like this will no longer be necessary. PEP 661 introduced a built-in sentinel class, which is very similar to the old pattern:

SENTINEL = sentinel("SENTINEL") assert SENTINEL is not None SENTINEL_2 = sentinel("SENTINEL_2") assert SENTINEL is not SENTINEL_2

Like an object sentinel, new sentinels should always be compared with is and not ==.

It will also play nicely with type hints (though in this case, a bare None would also work):

SENTINEL = sentinel("SENTINEL") five_characters: str | SENTINEL = SENTINEL while five_characters is SENTINEL: user_input = input("Enter five characters: ") if len(user_input == 5): five_characters = user_input

Before 3.14, this wouldn't have worked; you generally would've needed to introduce more overhead such as using a class rather than an object as your sentinel value.

Read Entire Article