I'm trying to implement a function that inverses a strictly monotonically increasing function. Given a value y
and a strictly monotonically increasing function f
, the function I'm trying to write would calculate f_inverse(y)
& return that.
Ideally this should work for any argument-set that has a total ordering defined on it. So it should work for int
, float
, tuple
etc.
So far I have this:
def invert_strictly_monotonic_function(y: Any, f: Callable[[Any], Any]) -> Any:
pass
I would like to replace the Any
with something like "SupportsOrdering"
- that would tell me that I could rely on the __eq__
& __lt__
methods inside this function.
But, of course, SupportsOrdering
doesn't exist in the typing
module.
So, how do I go about type-hinting this? Do I need to define my own protocol-type for this? And how would I make that work with something like @total_ordering
?
Since the typing module doesn't provide a comparable type, I think an alternative is defining our own custom type with the use of typing.protocol as the base class (for structural subtyping) requiring the comparer methods e.g. __lt__
and __eq__
with a user-defined SupportsOrdering
, inspired by these list of pre-defined protocols e.g. typing.SupportsRound
.
from __future__ import annotations
from abc import abstractmethod
from typing import Protocol, runtime_checkable, TypeVar
@runtime_checkable
class Comparable(Protocol):
@abstractmethod
def __lt__(self: SupportsOrdering, other: SupportsOrdering) -> bool:
pass
@abstractmethod
def __eq__(self: SupportsOrdering, other: object) -> bool:
pass
SupportsOrdering = TypeVar("SupportsOrdering", bound=Comparable)
class MyNonComparableClass:
def __init__(self, data):
self.data = data
class MyComparableClass:
def __init__(self, data):
self.data = data
def __lt__(self: MyComparableClass, other: MyComparableClass) -> bool:
return self.data < other.data
def __eq__(self: MyComparableClass, other: object) -> bool:
if not isinstance(other, MyComparableClass):
return NotImplemented
return self.data == other.data
def func(value: SupportsOrdering) -> SupportsOrdering:
return value
# Comparable built-ins
func(True)
func(1)
func(1.2)
func("a")
func("abc")
func([1, 2, 3]) # Sequence objects typically may be compared https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types
func((1, 2, 3))
func({1, 2, 3})
# Comparable user-defined class
func(MyComparableClass(1))
Output:
$ mypy script.py # Install mypy via <python3 -m pip install mypy>
Success: no issues found in 1 source file
# Non-comparable built-ins
func({"1": "1"}) # Order comparisons (‘<’, ‘<=’, ‘>=’, ‘>’) raise TypeError. https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
# Non-comparable user-defined class
func(MyNonComparableClass(1))
Output:
$ mypy script.py # Install mypy via <python3 -m pip install mypy>
script.py:57: error: Value of type variable "SupportsOrdering" of "func" cannot be "Dict[str, str]"
script.py:60: error: Value of type variable "SupportsOrdering" of "func" cannot be "MyNonComparableClass"
References:
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments