Is there a correct way to NewType
a diamond inheritance? The docs don’t seem to address it, and pyright seems to indicate no. At runtime, since NewType
is a fast return, python itself is extremely permissive of what goes as the second argument, so it’s hard to tell.
Motivation/MWE:
I have some array routines that lead to bugs because of numerical math, i.e. bugs that are tricky to find and troubleshoot. I was hoped introducing types for the math abstractions could help prevent mistakes and improve annotations. Take the following minimal example of some optimization alternates between updates to the bounds of some solution and the solution itself:
StateVector = NewType("StateVector", np.ndarray)
InequalityConstraint = NewType("InequalityConstraint", np.ndarray)
def new_bounds(current_state: StateVector, last_state: StateVector) -> InequalityConstraint:
"""Calculate next inner optimization bounds"""
def inner_optimization(f: Callable, x0: StateVector, lower_bound: InequalityConstraint) -> StateVector:
"""Optimize something, given x0 >= InequalityConstraint"""
IIUC, the type checker would cause an alarm if someone tried to use a StateVector
in place of an InequalityConstraint
, or if they used the latter in a math routine that required equality constraints. Moreover, these abstractions feel like they help clarify intent and provide an easy stepping stone to full-blown classes when additional functionality is needed, without prematurely adding features.
However, the problem is when my algorithm allows sparse matrices or some other duck-typing. A type alias to a Union can’t be NewType’d, at least according to Pylance/pyright:
Foo = NewType("Foo", int | str)
Pylance: Expected class as second argument to NewType
But I thought the second argument was a type, not a class, according to PEP 483 . Are diamond diagram subtypes possible without an actual subclass?