PEP 526 - var annotations and the spirit of python

Steven D'Aprano steve+comp.lang.python at
Thu Jul 5 05:59:55 EDT 2018

On Thu, 05 Jul 2018 17:34:55 +1200, Gregory Ewing wrote:

> Steven D'Aprano wrote:
>> but the type checker should infer that if you assign None to a variable
>> which is declared int, you must have meant Optional[int] rather than
>> just int.
> This seems to be equivalent to saying that *all* types are Optional, in
> which case what point is there in having Optional at all?

How do you reason that?

    x = 3

infers that x is an int, and only an int, not an optional int. An 
explicit hint, like:

    def func(x:int) -> something

requires func(x) to only accept int arguments, not int-or-None. But:

    def func(x:int = None) -> something

says to the type checker, in effect, "Yeah, I know I said an int, but I 
obviously also want to allow None because that's the default, so just 
deal with it, okay?"

You may or may not like the "Do What I Mean" aspect of this, you might 
question whether Optional[int] is really more common than Union[int, str] 
(for example). But I don't see how you go from this narrowly defined 
exceptional situation ("use a hint for one type and assign None on the 
same line") and conclude that "all types are optional".

>> Indeed, that's often the best way, except for the redundant type hint,
>> which makes you That Guy:
>>     x: int = 0  # set x to the int 0
> But you've shown in an earlier example that such a hint is *not* always
> redundant

But it is redundant in *that* example. Your hint is not giving any more 
information that what the reader, or type checker, can already infer.

These are useful:

    x: Any = 3  # x can be anything, but is currently an int

    x: int = None # x can be an int, or None

    x: Union[int, str] = 'abc'  # x can be either an int, or a
    # string, not just the string you see here

but this is redundant (unless you're forced to use a type-checker with no 
type inference at all):

    x: int = 3

Well duh of course its an int. That's what 3 is. An int. If it were a 
comment, you'd slap the writer around the head with a clue-stick:

    x = 3  # x is an int

> e.g.
>     x = 0
>     x = 2.5
> is legal and useful, 

Of course it is useful -- well, not *literally* that pair of lines, but I 
know what you mean -- but its probably an error.

Mypy allows ints wherever a float is expected:

    x = 0.0  # infer x is a float
    x = 1    # ints are compatible with floats

which probably isn't *strictly* valid (ints can exceed the range of 
floats), so I assume there's a switch to disable it. But the other way is 
an error:

    x = 0    # infer x is an int
    x = 2.5  # that's not an int!

Some type checkers may even enforce a strict separation of ints and 
floats. In any case, if you want to overrule the type checker, you can:

    x: Union[int, float] = 0
    x = 2.5

A better example would be:

    x = 0  # infers type int, or maybe type Number
    x = "foo"  # certainly not a number, so a type error

> whereas
>     x: int = 0
>     x = 2.5
> ought to be a type error.

It ought to be a type error even without the hint.

Steven D'Aprano
"Ever since I learned about confirmation bias, I've been seeing
it everywhere." -- Jon Ronson

More information about the Python-list mailing list