In the typing
module, the protocols SupportsInt
, SupportsFloat
, and SupportsComplex
are decorated with @runtime_checkable
. However, at runtime they produce false negatives, false positives, and inconsistent results when used to check built-in numeric types and equivalent NumPy numeric types.
Verify the Python version:
>>> import sys >>> sys.version_info[:2] (3, 8)
To make this file runnable with doctest
, we start with some imports:
>>> import numpy as np >>> from typing import SupportsComplex, SupportsFloat, SupportsInt
And some sample numbers:
>>> type(1+0j), type(np.complex64(1+0j)) (<class 'complex'>, <class 'numpy.complex64'>) >>> 1+0j, np.complex64(1+0j) ((1+0j), (1+0j))
SupportsInt
checks whether the type implements __int__
,
but that does not mean you can convert to int
.
Can I convert a complex number to an int
? This suggests yes:
>>> isinstance(1+0j, SupportsInt), isinstance(np.complex64(1+0j), SupportsInt) (True, True)
But actually, only a NumPy complex can be converted to an int
:
>>> int(np.complex64(1+0j)) 1
The code above "works" but you get a warning about converting to "real":
ComplexWarning: Casting complex values to real discards the imaginary part
Trying the same with a Python built-in complex
raises TypeError
:
>>> int(1+0j) Traceback (most recent call last): ... TypeError: can't convert complex to int
In the typing-sig mailing list, Guido explained that the built-in complex
implements __int__
only to provide a better error message.
>>> complex.__int__ <slot wrapper '__int__' of 'complex' objects>
While type checking, Mypy says a built-in complex
is inconsistent with SupportsInt
, but at runtime, the same complex
passes an isinstance
check with SupportsInt
.
On typeshed, there is no method __int__
in complex
. Mypy complains that complex
is inconsistent with SupportsInt
:
demo.py:16: error: Argument 1 to "to_int" has incompatible type "complex"; expected "SupportsInt"
(Similar to #1)
isinstance(c, SupportsFloat)
returns True
for both complex types,
but only the NumPy complex can actually be converted to float
:
>>> isinstance(1+0j, SupportsFloat), isinstance(np.complex64(1+0j), SupportsFloat) (True, True) >>> float(np.complex64(1+0j)) 1.0 >>> float(1+0j) Traceback (most recent call last): ... TypeError: can't convert complex to float
In the typing-sig mailing list, Guido explained that the built-in complex
implements __float__
only to provide a better error message.
(Similar to #2) While type checking, Mypy says a built-in complex
is inconsistent with SupportsFloat
, but at runtime, the same complex
passes an isinstance
check with SupportsFloat
.
On typeshed, there is no method __float__
in complex
. Mypy complains that complex
is inconsistent with SupportsFloat
:
demo.py:23: error: Argument 1 to "to_float" has incompatible type "complex"; expected "SupportsFloat"
Please see SupportsComplex_issue.rst