Options for a long term fix of the special case for float/int/complex

The exact language from PEP 484 is

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

There is also this example from the “Subtype relationships” section of PEP 483

A more formal example: Integers are subtype of real numbers. Indeed, every integer is of course also a real number, and integers support more operations, such as, e.g., bitwise shifts << and >>:

lucky_number = 3.14    # type: float
lucky_number = 42      # Safe
lucky_number * 2       # This works
lucky_number << 5      # Fails

unlucky_number = 13    # type: int
unlucky_number << 5    # This works
unlucky_number = 2.72  # Unsafe

(note: variable annotations were not supported until later with PEP 526, so this is using the special type comments)

From those two, it’s clear that the behavior of a type checker should be that an int can be assigned to a variable annotated as float or passed to a function argument annotated as float, but it doesn’t actually specify how that behavior should be implemented.

mypy uses heuristics that kind of treat int as a subclass of float but not always, while pyright moved to mostly doing the approach of “float in a type expression actually means float | int

There had already been an issue on the mypy issue tracker that suggested moving to that union approach. Carl Meyer (who has been working on Astral’s new type checker ty) opened an issue on the typing GitHub asking that the typing specification be clarified.

Jelle Zijlstra proposed that the typing specification be modified to explicitly specify the union approach, which mostly matches existing type checker results and the conformance test suite that goes along with the typing specification. He then opened a thread here on Discuss about that change, which then prompted this very discussion thread.

1 Like