How to type annotate a 5x5 array stored as a tuple (original) (raw)

I have a function that returns a 5x5 matrix of float values. The only way I’ve found to write an annotation for this is

tuple[
    tuple[float, float, float, float, float],
    tuple[float, float, float, float, float],
    tuple[float, float, float, float, float],
    tuple[float, float, float, float, float],
    tuple[float, float, float, float, float],
]

Is that really the best way of writing this?

Before anyone suggests it, giving it an alias is basically pointless, as it’s only explicitly named once in my code. I could do sonething like t5 = tuple[float, float, float, float, float] and then use tuple[t5, t5, t5, t5, t5], but it’s still incredibly repetitive.

Daverball (David Salvisberg) May 3, 2025, 3:51pm 2

That’s currently the only way we have. There have been some proposals for shorthand notations for homogenous fixed sized tuples in the past, but they never went anywhere, most likely because it’s not common enough of a use-case.

If the fact that the tuple is 2D is more important than the fact that it’s exactly 5x5, there’s obviously also the option of using the less precise tuple[tuple[float, ...], ...], but I suspect you already know that.

pf_moore (Paul Moore) May 3, 2025, 4:01pm 3

I hadn’t worked out the correct incantation, so thanks for that, but I’m not sure how much better it is. To be honest, though, this isn’t a program that really needs typing in any case. It’s a one-off simulation, so type checks offer some protection against bugs to offset the fact that it’s hard to test because the output is probabilistic. The main reason for the question was simply “surely this can’t be the best option we have?” 🙂

It does matter that the tuple is 5x5 (it’s indexed by an IntEnum value with 5 possibilities) but I doubt that Python’s typing is strong enough to check that anyway. So your form is probably sufficient for my needs.

One thought - if I’d made this into a NumPy array, would I be able to say it’s a 5x5 array with dtype float, or is numpy just as opaque to typing as tuples are?

Dutcho (Dutcho) May 3, 2025, 4:20pm 4

I’d suggest an alias:

T = TypeVar('T')
Quintuple: TypeAlias = tuple[T, T, T, T, T]
def function(m5x5: Quintuple[Quintuple[float]]) -> None:
    ...

Or in more recent Python:

type Quintuple[T] = tuple[T, T, T, T, T]
def function(m5x5: Quintuple[Quintuple[float]]) -> None:
    ...

Perhaps we should add homogeneous tuples to useful-types.

pf_moore (Paul Moore) May 3, 2025, 4:41pm 5

Thanks. I’ll remember that (as I said, for my immediate need, it’s not worth being that precise, but I may need it in the future).

It would be nice if generics could take integers as parameters, so you could do Multiple[T, 5], but I guess that’s too much to hope for at the moment.

a-reich (A Reich) May 4, 2025, 11:19pm 6

With numpy arrays, the most detailed annotation would be
np.ndarray[tuple[Literal[5],Literal[5]], np.dtype[np.float64]].

That said, I think the typing API exposed by numpy currently supports shapes a lot less well than dtypes, so if you were using a type checker it would not necessarily check or infer the correct shapes.

Can’t speak on the pure python side but I’ve always felt it was a code smell with numpy to require specific shapes.

I can get indicating the “major”-ness but I don’t see many people explicitly Fortran ordering with numpy.