|
| 1 | +# numpy |
| 2 | + |
| 3 | +```toml |
| 4 | +[environment] |
| 5 | +python-version = "3.14" |
| 6 | +``` |
| 7 | + |
| 8 | +## numpy's `dtype` |
| 9 | + |
| 10 | +numpy functions often accept a `dtype` parameter. For example, one of `np.array`'s overloads accepts |
| 11 | +a `dtype` parameter of type `DTypeLike | None`. Here, we build up something that resembles numpy's |
| 12 | +internals in order to model the type `DTypeLike`. Many details have been left out. |
| 13 | + |
| 14 | +`mini_numpy.py`: |
| 15 | + |
| 16 | +```py |
| 17 | +from typing import TypeVar, Generic, Any, Protocol, TypeAlias, runtime_checkable, final |
| 18 | +import builtins |
| 19 | + |
| 20 | +_ItemT_co = TypeVar("_ItemT_co", default=Any, covariant=True) |
| 21 | + |
| 22 | +class generic(Generic[_ItemT_co]): |
| 23 | + @property |
| 24 | + def dtype(self) -> _DTypeT_co: |
| 25 | + raise NotImplementedError |
| 26 | + |
| 27 | +_BoolItemT_co = TypeVar("_BoolItemT_co", bound=builtins.bool, default=builtins.bool, covariant=True) |
| 28 | + |
| 29 | +class bool(generic[_BoolItemT_co], Generic[_BoolItemT_co]): ... |
| 30 | + |
| 31 | +@final |
| 32 | +class object_(generic): ... |
| 33 | + |
| 34 | +_ScalarT = TypeVar("_ScalarT", bound=generic) |
| 35 | +_ScalarT_co = TypeVar("_ScalarT_co", bound=generic, default=Any, covariant=True) |
| 36 | + |
| 37 | +@final |
| 38 | +class dtype(Generic[_ScalarT_co]): ... |
| 39 | + |
| 40 | +_DTypeT_co = TypeVar("_DTypeT_co", bound=dtype, default=dtype, covariant=True) |
| 41 | + |
| 42 | +@runtime_checkable |
| 43 | +class _SupportsDType(Protocol[_DTypeT_co]): |
| 44 | + @property |
| 45 | + def dtype(self) -> _DTypeT_co: ... |
| 46 | + |
| 47 | +# TODO: no errors here |
| 48 | +# error: [invalid-type-arguments] "Type `typing.TypeVar` is not assignable to upper bound `generic[Any]` of type variable `_ScalarT_co@dtype`" |
| 49 | +# error: [invalid-type-arguments] "Type `typing.TypeVar` is not assignable to upper bound `generic[Any]` of type variable `_ScalarT_co@dtype`" |
| 50 | +_DTypeLike: TypeAlias = type[_ScalarT] | dtype[_ScalarT] | _SupportsDType[dtype[_ScalarT]] |
| 51 | + |
| 52 | +DTypeLike: TypeAlias = _DTypeLike[Any] | str | None |
| 53 | +``` |
| 54 | + |
| 55 | +Now we can make sure that a function which accepts `DTypeLike | None` works as expected: |
| 56 | + |
| 57 | +```py |
| 58 | +import mini_numpy as np |
| 59 | + |
| 60 | +def accepts_dtype(dtype: np.DTypeLike | None) -> None: ... |
| 61 | + |
| 62 | +accepts_dtype(dtype=np.bool) |
| 63 | +accepts_dtype(dtype=np.dtype[np.bool]) |
| 64 | +accepts_dtype(dtype=object) |
| 65 | +accepts_dtype(dtype=np.object_) |
| 66 | +accepts_dtype(dtype="U") |
| 67 | +``` |
0 commit comments