Clarification for PEP 604: is `foo: int | None` to replace all use of `foo: Optional[int]`

That’s a fair point. Although just to be clear, I’m not particularly in favour of PEP 661 style sentinels - if I wanted something more opaque than None, what I would typically use would be something more traditional like a singleton instance of object() - and that can’t be typed properly by any form of int | <something> annotation.

The reality is, I think, that it’s not possible to easily annotate the type of a function that’s intended to be called only as either foo() or as foo(int) - and by “easily” I mean, “without resorting to something like overloads, which I’m aware of but have no idea how to use”. And yet it’s trivial to define such a function in untyped Python, simply by using a default of None that isn’t intended to be ever supplied by the user, just like a non-integer argument is never intended to be passed. However, by promoting int | None as the “correct” way to annotate this, we normalise the idea that placeholders are part of the public interface of the function (which in turn leads to the interest in adding explicit sentinel types á la PEP 661).

All of which has drifted very far from the original point of this thread. But I’m afraid I’m still of the opinion that, given that there are two semantically identical ways of spelling “an argument that is either an integer which can be omitted, or None (meaning ‘use the default’)” - Optional[int] = None and int | None = None, then I prefer the former, for the following reasons:

  1. It avoids the ugly | None = None repetition.
  2. It focuses on the “can be omitted” semantics (which is what matters to me) rather than the “can be None” semantics.

Neither form allows using a sentinel other than None, and both still require the = None default. So I don’t see a distinction based on either of those aspects.

Conclusion: I’m not in favour of seeing Optional get deprecated, nor of | None being pushed as the “one true way” of annotating this type of argument. However, I’m not sure anyone is actually proposing to do either of those things[1] so I’m happy enough with the current status quo.


  1. Although I’m getting the impression that “int | None = None is preferred” could easily drift into “is recommended” and from there to “is the correct way”, and then to being enforced by linters… ↩︎

2 Likes