I’m not sure if that was directed at me or the OP, but personally, I don’t think “implicit optional” is a solution either, as it’s special-casing None
, which doesn’t interact well with explicit Sentinel
objects (such as those proposed in PEP 661).
I disagree. The API is what I document it as, and if I don’t document None
as a valid argument, it’s not supported. It doesn’t matter if tools display some of the internals, that doesn’t make them supported. But type checkers are supposed to help me validate that my code is used as intended, and so I want them to ensure that users either don’t supply the argument, or supply an integer value, as that matches the API I have designed. (Or they explicitly suppress the check to confirm they know what they are doing).
I’m fine with the answer “type checkers can’t support that API” (although I’ll note that won’t stop me from writing APIs like that), but I’m not fine with being told the current situation addresses the issue - it doesn’t, it just pretends the issue doesn’t exist. And I don’t want to suggest that this is a fatal flaw with type annotations, or anything like that - they do an amazing job of capturing far more of the “programmer’s intent” than I honestly ever expected. But they still fall short in some cases, typically where Python is particularly dynamic (although that’s not really the issue in this case). In those cases, I think we all need to remember that typing is intended to be “gradual” - use it when it helps, but don’t make it a straitjacket that prevents using features of the language that work, and have been successfully used without type annotations for years.
Practicality vs purity is fine here, if int | None
is good enough, then I’m not going to argue. But it’s still only “good enough”, and it doesn’t always capture my intent, whereas in some cases Optional[int] = None
does, and I’d happily use it (note, I wouldn’t omit the = None
and I understand that doing so is misleading). Again, practicality vs purity (in this case, saying what I mean even though it technically could be misused).
But going back to the original question, as a result of this discussion, I stand by what I said that both int | None
and Optional[int]
say the same thing, and which to use is (and should remain) a matter of developer choice. I will add the caveat that neither is perfect - int | None = None
implies a commitment that wasn’t intended, whereas Optional[int] = None
is misleading if the default value gets removed in a subsequent code change.