-
I'm trying to add type annotations to the following function, but I can't figure out anything that works: def signum(x):
"""
Returns the sign of ``x``: 1 if positive, -1 if negative, 0 if zero. For
complex numbers, returns the number with the same phase angle and magnitude
1.
"""
if x == 0:
return 0
else:
return x / abs(x) All of the following mypy runs were performed with mypy 0.961 on Python 3.9.13 on macOS 11.6.6 with the following config: [mypy]
allow_incomplete_defs = False
allow_untyped_defs = False
ignore_missing_imports = True
no_implicit_optional = True
implicit_reexport = False
local_partial_types = True
pretty = True
show_error_codes = True
show_traceback = True
strict_equality = True
warn_redundant_casts = True
warn_return_any = True
warn_unreachable = True First attempt: from numbers import Number
def signum(x: Number) -> Number:
... Results:
Second attempt: from typing import SupportsAbs, TypeVar
T = TypeVar("T", bound=SupportsAbs)
def signum(x: T) -> T:
... Results:
Third attempt: from typing import Protocol, SupportsAbs, TypeVar
T = TypeVar("T", bound="SupportsSignum")
class SupportsSignum(SupportsAbs[T]):
def __truediv__(self, other: T) -> T:
...
def signum(x: T) -> T:
... Results:
Fourth attempt: from typing import Protocol, TypeVar
T = TypeVar("T", bound="SupportsSignum")
class SupportsSignum(Protocol):
def __abs__(self: T) -> T:
...
def __truediv__(self: T, other: T) -> T:
...
def signum(x: T) -> T:
"""
Returns the sign of ``x``: 1 if positive, -1 if negative, 0 if zero. For
complex numbers, returns the number with the same phase angle and magnitude
1.
"""
if x == 0:
return x
else:
return x / abs(x) Results:
Now I'm out of ideas. Help? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Sorry for the late reply. Looking at the docstring, the function is intended to be used only with floats and complex numbers, not with arbitrary types that happen to support absolute value and division (e.g. a 3D vector from some library). So you have two cases, either a float is passed in and the result is a float, or a from __future__ import annotations # for "|" syntax
from typing import overload
@overload
def signum(x: float) -> float: ...
@overload
def signum(x: complex) -> complex: ...
def signum(x: float | complex) -> float | complex:
if x == 0:
return 0
else:
return x / abs(x)
reveal_type(signum(5.2)) # float
reveal_type(signum(0)) # float
reveal_type(signum(1+2j)) # complex
reveal_type(signum(0+0j)) # complex Note that it's fine to return This can be shortened slightly with a from typing import TypeVar
T = TypeVar("T", float, complex)
def signum(x: T) -> T:
if x == 0:
return 0
else:
return x / abs(x)
reveal_type(signum(5.2)) # float
reveal_type(signum(0)) # float
reveal_type(signum(1+2j)) # complex
reveal_type(signum(0+0j)) # complex This is pretty close to your second attempt, but your |
Beta Was this translation helpful? Give feedback.
Sorry for the late reply. Looking at the docstring, the function is intended to be used only with floats and complex numbers, not with arbitrary types that happen to support absolute value and division (e.g. a 3D vector from some library). So you have two cases, either a float is passed in and the result is a float, or a
complex
is passed in and the result is acomplex
.