Reputation: 161
Why does "mypy" consider "int" as a subtype of "float"? A subtype shall support all methods of its supertype, but "float" has methods, which "int" does not support:
test.py:
def f(x : float) -> bool:
return x.is_integer()
print(f(123.0))
print(f(123))
The static type checker accepts passing an "int" argument for a "float" parameter:
(3.8.1) myhost% mypy test.py
Success: no issues found in 1 source file
But this does not guarantee, that there are no errors at runtime:
(3.8.1) myhost% python test.py
True
Traceback (most recent call last):
File "test.py", line 5, in <module>
print(f(123))
File "test.py", line 2, in f
return x.is_integer()
AttributeError: 'int' object has no attribute 'is_integer'
because "float" has additional methods, which "int" does not have.
Upvotes: 14
Views: 2637
Reputation: 19209
'Why does "mypy" consider "int" as a subtype of "float"?'
Because practicality has so far been considered to beat purity here. This is not to say that one could not propose that typing define a Scalar type that would include ints and floats but only be valid for arithmetic operations.
Note that int / int was changed in 3.0 so that float(int / int) == float(int) / float(int), to make int and float arithmetic consistent for equal int and float values.
Note also that a type-check passing does not mean no runtime errors: division by zero and overflow are still possible, as well as many others.
UPDATE: as of Python 3.8, ints now have an .as_integer_ratio method, and as of 3.12, ints will gain an .is_integer method, leaving only the specialized .hex and .from_hex as float-only.
Upvotes: 5
Reputation: 5054
As @juanpa.arrivillaga pointed out, the explanation is on https://mypy.readthedocs.io/en/latest/duck_type_compatibility.html.
A subtype shall support all methods of its supertype, but "float" has methods, which "int" does not support
int
is not a subtype of float
, so it doesn't have to support methods of float
.
The mechanism is good because passing integer values shouldn't cause errors, unless you really want them as in your example. You explicitly tried to use a method which doesn't exist. In common situations, we only make arithmetic operations on numbers, so a problem rarely exists and you can always avoid it by adding .0
as you wrote.
It is a common behavior in most languages to assume that int
is a special case of float
, consider for example C++ int
to float
implicit conversion.
Upvotes: -2