242

What's the difference of using List, Tuple, etc. from typing module:

from typing import Tuple

def f(points: Tuple):
    return map(do_stuff, points)

As opposed to referring to Python's types directly:

def f(points: tuple):
    return map(do_stuff, points)

And when should I use one over the other?

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
Markus Meskanen
  • 19,939
  • 18
  • 80
  • 119
  • 3
    Give this a read, I found myself asking the same question you had until I read this. https://docs.python.org/3/library/typing.html – MooingRawr Sep 12 '16 at 20:22
  • 1
    [Tuple, used by listing the element types. for example Tuple\[int, int, str\]](https://www.python.org/dev/peps/pep-0484/#the-typing-module) – Mazdak Sep 12 '16 at 20:24
  • Possible duplicate http://stackoverflow.com/questions/37087457/difference-between-defining-typing-dict-and-dict – Mazdak Sep 12 '16 at 20:29

2 Answers2

282

Until Python 3.9 added support for type hinting using standard collections, you had to use typing.Tuple and typing.List if you wanted to document what type the contents of the containers needed to be:

def f(points: Tuple[float, float]):
    return map(do_stuff, points)

Up until Python 3.8, tuple and list did not support being used as generic types. The above example documents that the function f requires the points argument to be a tuple with two float values.

typing.Tuple is special here in that it lets you specify a specific number of elements expected and the type of each position. Use ellipsis if the length is not set and the type should be repeated: Tuple[float, ...] describes a variable-length tuple with floats.

For typing.List and other sequence types you generally only specify the type for all elements; List[str] is a list of strings, of any size. Note that functions should preferentially take typing.Sequence as arguments and typing.List is typically only used for return types; generally speaking most functions would take any sequence and only iterate, but when you return a list, you really are returning a specific, mutable sequence type.

If you still need to support Python 3.8 or older code, you should always pick the typing generics even when you are not currently restricting the contents. It is easier to add that constraint later with a generic type as the resulting change will be smaller.

If you are implementing a custom container type and want that type to support generics, you can implement a __class_getitem__ hook or inherit from typing.Generic (which in turn implements __class_getitem__).

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • What about the advantage of List[] over []. If personally find the first nicer but I have no argmuent in a code review for example :) – Samos May 25 '20 at 14:43
  • 4
    @Samos Both would be utilizing [PEP 3107](https://www.python.org/dev/peps/pep-3107/) but [] isn't described in [PEP 484](https://www.python.org/dev/peps/pep-0484/), or [the extension PEPs](https://docs.python.org/3/library/typing.html). By not following the standard you don't get the benefits of tools that _only_ follow the standard. E.g. mypy may not work. – Peilonrayz May 25 '20 at 15:38
  • 1
    "Note that functions should preferentially take typing.Sequence as arguments and typing.List is typically only used for return types; generally speaking most functions would take any sequence and only iterate, but when you return a list, you really are returning a specific, mutable sequence type." Why shouldn't functions return a sequence? – kolistivra Aug 12 '20 at 16:24
  • 5
    @kolistivra: because that limits what a caller can do with the returned list object. Anything that requires, say, a *mutable* sequence won't accept the return value. Or if something specifically requires a list for whatever reason, you can use the return value either. Set your return value as specific as you can make it. – Martijn Pieters Aug 12 '20 at 21:00
  • 2
    Don't forget to `from typing import Tuple` – Sparkofska Sep 27 '21 at 17:40
  • Per the samples in the typing documentation `list[float]` is also valid, see: https://docs.python.org/3/library/typing.html#type-aliases – haridsv Dec 31 '21 at 16:10
  • @haridsv: that's [new in Python 3.9](https://docs.python.org/3/whatsnew/3.9.html#type-hinting-generics-in-standard-collections), which came out in October 2020, so 4 years after I wrote this answer. I've updated it to reflect the changed landscape. – Martijn Pieters Jan 19 '22 at 22:00
  • Your last paragraph seems incorrect. "To implement custom generic classes that can be parameterized at runtime and understood by static type-checkers, users should either inherit from a standard library class that already implements `__class_getitem__()`, or inherit from `typing.Generic`, which has its own implementation of `__class_getitem__()`." `__class_getitem__` is mostly an implementation detail to work around the limitations of metaclasses. – Peilonrayz Jan 19 '22 at 23:54
  • @Peilonrayz: I don't see how that makes my last sentence incorrect. I can add a mention of `typing.Generic`, but to dismiss `__class_getitem__` as an implementation detail is not a fair characterisation. – Martijn Pieters Jan 20 '22 at 08:02
147

From Python 3.9 (PEP 585) onwards tuple, list and various other classes are now generic types. Using these rather than their typing counterpart is now preferred. From Python 3.9 you can now just do:

def f(points: tuple[float, float]):
    return map(do_stuff, points)

If you don't need to evaluate your type hints then you can use this syntax in Python 3.7+ due to PEP 563.

from __future__ import annotations


def f(points: tuple[float, float]):
    return map(do_stuff, points)

You should always pick then non-typing generic whenever possible as the old typing.Tuple, typing.List and other generics are deprecated and will be removed in a later version of Python.

Importing those from typing is deprecated. Due to PEP 563 and the intention to minimize the runtime impact of typing, this deprecation will not generate DeprecationWarnings. Instead, type checkers may warn about such deprecated usage when the target version of the checked program is signalled to be Python 3.9 or newer. It's recommended to allow for those warnings to be silenced on a project-wide basis.

The deprecated functionality will be removed from the typing module in the first Python version released 5 years after the release of Python 3.9.0.

Peilonrayz
  • 3,129
  • 1
  • 25
  • 37
  • 6
    *"Using these rather than their typing counterpart is now preferred."* For public python libraries, doesn't it make sense to continue using the `typing` generics for 4 years after the release of Python 3.9.0? – Mateen Ulhaq Jan 12 '21 at 02:36
  • 1
    Not disagreeing @MateenUlhaq, but can you share why it makes sense? – MinneapolisCoder9 Jan 19 '22 at 22:44
  • 2
    @MonkeySeeMonkeyDo So the libraries work on Python 3.0+. Whilst you can just manually perform what `__future__.annotations` does (`def f(points: "tuple[float, float]")`) the code won't work if you need to evaluate the type hints at run time. – Peilonrayz Jan 19 '22 at 22:52
  • Note that you must use square brackets - `[` and `]` - for typing. If you try `tuple(float, float)` you'll get `TypeError: tuple expected at most 1 argument, got 2` – codeananda Jan 31 '23 at 13:26
  • It depends whether you're focussed on legacy support or future longevity. The sooner you move onto the new canonical forms the sooner your code is proofed for future upgrades where the old forms are depreciated. Surely you decide on a Python version you're going to use and use that version? Why would you deploy Python 3.10 and still code in Python 3.8? – NeilG Aug 23 '23 at 05:18