Why does a type hint `float` accept `int` while it is not even a subclass?
Asked Answered
S

1

13

On the one hand, I have learned that numbers that can be int or float should be type annotated as float (sources: PEP 484 Type Hints and this stackoverflow question):

def add(a: float, b: float):
    return a + b

On the other hand, an int is not an instance of float:

  • issubclass(int, float) returns False
  • isinstance(42, float) returns False

I would thus have expected Union[int, float] to be the correct annotation for this use case.

Questions:

  • What is the reason for that counter-intuitive behaviour? Does type hinting follow different mechanics than class comparisons (for instance in some case a "lossless casting" rule or so)?
  • Are int/float a special case in type annotations? Are there other examples like this?
  • Is there any linter that would warn me about Union[float, int] if this is an unintended use?
Scholasticate answered 4/7, 2020 at 5:54 Comment(0)
C
13
  • Are int/float a special case in type annotations?

float is a special case. int is not. PEP 484 says, in the paragraph below the one referenced by the link in your question:

when an argument is annotated as having type float, an argument of type int is acceptable;

So accepting int where float is annotated is explicitly a special case, independent of the way annotations generally deal with a class hierarchy.

Are there other examples like this?

Yes, there's at least one other special case. In that same paragraph PEP 484 goes on to say:

for an argument annotated as having type complex, arguments of type float or int are acceptable.

  • Is there any linter that would warn me about Union[float, int] if this is an unintended use?

Union[float, int] is perfectly fine.

The special treatment of a float annotation is just a convenience (PEP 484 calls it a "shortcut") to allow people to avoid writing out the long-winded Union[float, int] annotation, because arguments that can be a float or an int are very common.

Contribute answered 4/7, 2020 at 22:13 Comment(2)
Yes pythons type annotations do make pragmatic compromises. Another example is annotating with None when it should be NoneType or somethingCurcio
Thanks! So my fallacy lies in the assumption that float should be preferred over Union[int, float]. It is totally fine that I use the more explicit Union[int, float] - right? :)Scholasticate

© 2022 - 2024 — McMap. All rights reserved.