Specify length of Sequence or List with Python typing module
Solution 1
You can't. A list is a mutable, variable length structure. If you need a fixed-length structure, use a tuple instead:
Tuple[float, float, float, float, float, float, float, float, float, float]
Or better still, use a named tuple, which has both indices and named attributes:
class BunchOfFloats(NamedTuple):
foo: float
bar: float
baz: float
spam: float
ham: float
eggs: float
monty: float
python: float
idle: float
cleese: float
A list is simply the wrong data type for a fixed-length data structure.
Solution 2
So far, only tuples support specifying a fixed number of fields and it has no short-cut for a fixed number of repetitions.
Here's the definition and docstring from the typing module:
class Tuple(tuple, extra=tuple, metaclass=TupleMeta):
"""Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
Example: Tuple[T1, T2] is a tuple of two elements corresponding
to type variables T1 and T2. Tuple[int, float, str] is a tuple
of an int, a float and a string.
To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
"""
__slots__ = ()
def __new__(cls, *args, **kwds):
if _geqv(cls, Tuple):
raise TypeError("Type Tuple cannot be instantiated; "
"use tuple() instead")
return _generic_new(tuple, cls, *args, **kwds)
Since lists are a mutable, variable-length type, it doesn't make any sense to use a type declaration to specify a fixed size.
Solution 3
Annotated
can be handy here. It allows you to specify arbitrary metadata to type hints:
Annotated[List[float], 3]
Solution 4
When also confronted with the same problem, I was not happy seeing Martijn Pieters answer. Since I wanted a "fast" and "easy" way to solve this problem.
So I tried the other suggestions listed here first.
Note: I used VSCode with Pylance as Language Server
Zaffys answer was my favorite
def demystify(mystery: Annotated[Tuple[int], 6]):
a, b, c, d, e, f = mystery
print(a, b, c, d, e, f)
Hint for the function then looks like this: demystify: (mystery: Tuple[int]) -> None
Also I get a Pylance Error Tuple size mismatch: expected 6 but received
for the line a, b, c, d, e, f = mystery
Next I tried Tuple[6 * (int, )]
which was mentioned by balu in the comments of Martijn Pieters answer
def demystify(mystery: Tuple[6 * (int,)]):
a, b, c, e, f, g = mystery
print(a, b, c, e, f, g)
Resulting in the same Pylance Error as before.
Hint for the function was this: demystify: (mystery: Tuple[Tuple[Type[int], ...]]) -> None
Going back to writing down the expected length:
def demystify(mystery: Tuple[int, int, int, int, int, int]):
a, b, c, e, f, g = mystery
print(a, b, c, e, f, g)
This resolved the Pylance Error, and got me a "clear" function hint: demystify: (mystery: Tuple[int, int, int, int, int, int]) -> None
But just like John Brodie, I was not happy with this solution.
Now back to the, at first, unwanted answer:
class MysteryType(NamedTuple):
a: int
b: int
c: int
d: int
e: int
f: int
g: int
def demystify(mystery: MysteryType):
print(*mystery)
The function hint now seems more mystic: demystify: (mystery: MysteryType) -> None
but creating a new MysteryType gives me all the information I need: (a: int, b: int, c: int, d: int, e: int, f: int, g: int)
Also I can use the MysteryType in other methods and functions without the need of counting the type hints.
So, to make a long story short and paraphrase the Zen of Python:
NamedTuples are one honking great idea -- let's do more of those!
![Admin](/assets/logo_square_200-5d0d61d6853298bd2a4fe063103715b4daf2819fc21225efa21dfb93e61952ea.png)
Admin
Updated on October 07, 2021Comments
-
Admin over 2 years
I'm giving the Python
typing
module a shot.I know that it's valid to specify the length of a
List
like the following*:List[float, float, float] # List of 3 floats <-- NOTE: this is not valid Python
Is there any shorthand for longer lists? What if I want to set it to 10 floats?
List[float * 10] # This doesn't work.
Any idea if this is possible, this would be handy.
*NOTE: It turns out that supplying multiple arguments to
Sequence[]
(and its subclasses) in this manner is currently NOT valid Python. Furthermore, it is currently not possible to specify aSequence
length using thetyping
module in this way. -
Admin almost 7 yearsThanks Raymond, clear enough. While both answers I've received on here are accurate and clarifying, I'm still not 100% sure on the best way to hint for functions that really need set length Sequence input. I suppose that just putting this in the docstring isn't too bad, but that seems like a shame. (I'm really enjoying how PyCharm picks up on these hints in the generated help for each method)
-
Rick over 6 years"so far..." Are there any plans for specifying a fixed-length, mutable sequence
Generic
in thetyping
module at some point? -
Tomasz Bartkowiak almost 5 yearsIf you're using tuple you can also use literal ellipsis, i.e.
Tuple[int, ...]
as per PEP484 -
Martijn Pieters almost 5 years@TomaszBartkowiak: that's the opposite of what is being asked. Yes, you can declare a tuple of variable length containing a single type that way. But that's not a fixed size.
-
Matt almost 4 yearsSometimes you want a mutable container that is of fixed length. E.g. if you want to init the container items to None, but then update the items with new values. But the container would still remain fixed in size.
-
Martijn Pieters almost 4 years@Matt: sure, but there is no built-in Python type that lets you do that so no type hints either.
-
balu about 3 yearsFYI @MartijnPieters' first suggestion can be abbreviated as
Tuple[10 * (float, )]
, which I find rather short and neat, as it expresses its purpose very clearly. -
Timmmm about 3 yearsTypescript allows you to do this (i.e. hint arrays as if they were tuples) and it works fine. In practice this is really annoying because Python users often do use lists as if they were tuples, and I would really like to be able to add type hints to them.
-
Martijn Pieters about 3 years@Timmmm: That's because JavaScript doesn't have tuples, so Typescript had to invent them from whole cloth.
-
Timmmm about 3 yearsThat is the original reason, yes. It isn't an argument for not supporting that feature though.
-
binaryfunt about 3 years@balu Are you sure you can do
Tuple[10 * (float, )]
? That isn't allowed in Python 3.7 at least -
balu about 3 years@binaryfunt I don't have access to Python 3.7 right now but it works for me on both Python 3.6.13 and Python 3.8.5, so I guess it should be working on Python 3.7, too. What is the error you're getting?
-
binaryfunt about 3 years@balu
Invalid type comment or annotation [valid-type]
. I get that for the declarationa: Tuple[10 * (float, )]
. mypy 0.812, python 3.7.0 (GCC 7.3.0) -
balu about 3 years@binaryfunt Sounds like a Mypy issue, not a Python one. Maybe you should take this up with the Mypy developers?
-
Daniel over 2 yearsIt's not possible with a list, but I would like to point out that it does make sense, for the same reasons that TypedDict exists - schema/validation libraries.
-
Nulano over 2 years
Annotated[Tuple[int], 6]
means a tuple with a single int (with a 6 as metadata). Zaffy's answer isAnnotated[List[int], 6]
which is an arbitrary list of ints (with a 6 as metadata). Ideally a type checker would be able to read the 6 to understand that you want a fixed-sized list, but this is not a standard way to specify it. -
Alex over 2 yearsThanks for pointing this out. When using
Annotated[List[int], 6]
there will - of course - no error be shown. Still I don't get proper type hints in VSCode with Pylance as Language Server. So I would still stick with the NamedTuple solution. Yet theAnnotated[List[int], 6]
might work well in other code editors. -
pabouk - Ukraine stay strong about 2 yearsThanks, it is good to know that
typing.Annotated
exists. --- IMHO the answer should explain that the added integer3
works just like a comment attached to the annotated variable. You have to write your own tools to actually use the additional annotation. --- Besides that, adding just an integer number alone will make the additional annotation ambiguous. It would be better to create a structure to be able to express the context - something like:Annotated[List[float], Length[3]]