Typing question - List vs list

hey y’all! i have a general question about typing best practices. it was my understanding (perhaps wrong) that when you add typing you import things like List from the typing module and use those to type

Thus below is a correct annotation for a list of strings.
List[str]

But in a review today i was told that
list[str] is the newer better way.

And now i’m confused as to what is best practice for typing. any thoughts? :pyos_gif:
Also where is the final word on what typing practices are best practices now? would that be in a pep or where would we find that authoritative information? i see this but it’s not the most user friendly language!

Edit: i didn’t realize mypy docs were so good. it seems like

python 3.9+ → user lowercase list, dict, etc AND use | not Union
python 3.8- → using typing library Dict, List, etc

i’m still curious why there are so many different options based on Python versions.

1 Like

i’m still curious why there are so many different options based on Python versions.

I’m sure there’s nuance I don’t know about but IIUC it has to do with when PEPs were added to extend typing, e.g. for being able to use built-ins directly we needed PEP 585 – Type Hinting Generics In Standard Collections | peps.python.org

Static typing as defined by PEPs 484, 526, 544, 560, and 563 was built incrementally on top of the existing Python runtime and constrained by existing syntax and runtime behavior. This led to the existence of a duplicated collection hierarchy in the typing module due to generics (for example typing.List and the built-in list).

This PEP proposes to enable support for the generics syntax in all standard collections currently available in the typing module.

@NickleDave thank you!

How would scientists find out about these things if they didn’t want to wade through a sea of peps? those documents are not the most fun to read nor are they particularly accessible. i’m working with someone on typing right now for stravalib for instance. and i finally found the mypy docs which were pretty darn good. but is that where people should go for that information? it doesn’t seem like the best spot for authoritative standards around typing!

1 Like

i finally found the mypy docs which were pretty darn good. but is that where people should go for that information? it doesn’t seem like the best spot for authoritative standards around typing!

Couldn’t agree more!
I haven’t yet found any walkthroughs for “getting started with type hints” that also clarify why things are the way they are for some of the surprising bits

I seem to remember this PyCon talk came up in Slack?

But AFAICT it’s pretty high level (and is from 2018)

1 Like

It looks like you have found your answer which is that yes, the community prefers lowercase builtins over typing imports so long as the Python version supports it. This goes for builtin objects, list and dict and so on, using | over Union, and | None over Optional[].

As for resources, I believe that the mypy docs may still be the best place to learn about all things typing in Python, but there is also Static Typing with Python — typing documentation as well as the language docs typing — Support for type hints — Python 3.11.4 documentation which do a good job. There has also been a steady stream of talks about typing; hard to recommend just one as it is not enough time to go over all of typing but instead usually focus on one dimension - such as the new protocol class.

1 Like

Ok so it SEEMS that | was only added in python 3.10. so i went through a module and removed all Union and Optional and now it seems tests are breaking in python < 3.9 and we aren’t ready to drop 3.9 yet.

eeks. so we can use builtins for 3.8+ using from __future__ import annotations but we need to either keep Optional / Union or use quotes for those annotations to force them as strings in Python < 3.9

does that seem correct to y’all? this is really a lot of detail that would be hard for a scientist to keep track of (as i’m finding!!)

Typing has evolved with each Python release and 3.10+ would be my recommended rules.
Another example of a popular library that uses typing for data validation is pydantic. Welcome to Pydantic - Pydantic

We used it with FastAPI with great success over the last 3 years. You will continue to see it grow in the coming years.

My rule of thumb, in general, is for prototyping in notebooks that I skip using typing. When I plan to move something to production or wider sharing, I will add typing for clarity and maintainability.

In general, I prefer typing as part of the declarations instead of docstrings since they are less prone to get outdated when code changes.

1 Like

I would not use from __future__ import annotations as I don’t think it is doing what you think. It is implicitly turning list into "list" not making list a valid type. And it’s a backwards incompatible change that is still in question if it will ever become default behaviour PEP 563 – Postponed Evaluation of Annotations | peps.python.org.

I was just talking to a colleague about this and if you really want to use the latest typing idioms with a wide range of Python versions there are some options. But I think none of them would let you run the type checker under e.g. 3.9.

  • stringify all type annotations
  • guard type definitions behind if TYPE_CHECKING
  • use .pyi files for typing

Thank you both!!

@willingc we actually use pydantic in stravalib! we create a model from an example API json file and then populate it into classes. it’s been a bit tricky typing some of it because of that!

we ended up using the from future for python 3.8 which we are still supporting but i’m sure we’ll be bumping to 3.9-3.12 in the future which will then be easy. we are builtins for list, dict but sticking with Union and Optional as that seems to be python 3.10+. we’re following mypy suggestions at this point.

eeks i’m not ready to add stubs to our code :exploding_head:
it’s a bit confusing to navigate this space TBH!

@ucodery how is stringifying any different than importing from __future__. my understanding was it is just converting to string so how is that different from manually adding string annotations to types?

I don’t know of any way that the resulting annotation is different between a string or __future__.annotations. It’s just a different way of getting there. The import will have to be done per module, but affects all annotations, while strings can be mixed in with real type objects in the same file. Also string annotations have always been validated for every version of Python that has had annotations.

1 Like

ahhh ok i see what you’re saying!!
i find those peps to be really hard to digest. i don’t really read them and clearly understand what the best approach should be. i think we decided in stravalib that consistency was best and we just went with lowercase and __future__.annotations . but also the person who started the typing knows much more than I about it and i’m intentionally working on a module to learn from them.