What You Need to Know About Python 3.12 Typing – Part I
Prepare to be amazed
I am truly amazed by the new Python 3.12 and after reading this article you will be as well.
I promise.
In the previous article, we discussed three things you should know about Python 3.12; in this one, we focus 100% on the new typing library.
So this is what we are going to cover in this article:
- Python typing library
- Generic types in Python’s typing — TypeVar
- The type statement
Let’s start from the basics, as I always like to do. I’ll start with a brief overview of the typing library to ensure we are all on the same page.
So, without further ado, let's get coding.
Python typing library
Unlike Java and many other programming languages, Python is a dynamic programming language.
This means that you can decide whether to type your variable or not:
name: str = "Yanick" # explicitly typing my variable
# defining the parameter type and the return type of our function
def say_hello(name: str) -> None:
print(f"Hello, {name}")In our previous example, we decided that it’s a good idea to keep our code type to make it more readable and with that, easy to maintain.
But, that doesn’t necessarily mean that if we send other things rather than string as an argument to our function say_hello it will not work.
Type hints will be used by type checkers, linters, and your IDE to warn and prevent you from passing or assigning inconsistent types to functions and variables.
“It should also be emphasized that Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.” — PEP 484
Typing hints in Python is more like an informational thing you add to your code.
It makes your code easier to read, easier to use and integrate with other systems, easier to document, and easier to maintain.
I use it every chance I get with a healthy balance between strict and flexible typing to take the best of both worlds.
“Type hints are the biggest change in the history of Python since the unification of types and classes in Python 2.2, released in 2001.” — Fluent Python (Ramalho. L, 2022)
— What happens if you don’t explicitly type your code?
If you don’t explicitly type your code, Python will do it for you at runtime through the __annotations__ attribute.
— What if you want to make type-checking more strict?
Python typing was strongly inspired by mypy according to PEP 484.
“The proposal is strongly inspired by mypy.”
Mypy is a static type checker that allows you to add type hints gradually as you go.
Unlike regular typing in Python, using mypy will raise an exception or error when the type is not right.
# defining the parameter type and the return type of our function
def say_hello(name: str) -> None:
print(f"Hello, {name}")If you call our function say_hello with the wrong type with mypy, we will get a clear error saying that we sent the wrong type argument:
say_hello(1) # call our function with int instead of str
# error: Argument 1 to "say_hello" has incompatible type "int"; expected "str"To start using mypy you simply install it using pip:
pip install mypy # install mypy to your virtualenv
mypy <my-script>.py # run your python code using mypyBefore you install it make sure you’re in a virtual environment to avoid breaking your Python on your machine in case of any issue.
As I said previously, mypy allows you to gradually type your code as you go. You don’t have to type every function if you don’t want to.
But in case you want to make sure that every single function in your code is typed, mypy can also make this even strictly.
We have a set of commands we can use to make mypy more strict regarding type-checking. One of them is — disallow-untyped-defs and — disallow-incomplete-defs.
The command — disallow-untyped-defs reports an error when it finds a function that has no annotation or is partially annotated:
def funct_a(name: str, age): # report error. function partially annotated ❌
...
def funct_b(name, age): # report error. function not annotated ❌
...On the other hand, — disallow-incomplete-defs will report an error only when a function is partially annotated:
def funct_a(name: str, age): # report error. function partially annotated ❌
...
def funct_b(name, age): # no error. ✅
...Perfect. Now that we have covered some of the basics of Python typing, I think we can delve into the new things from Python 3.12 as promised.
Generic types in Python’s typing
In Computer Science we define data types as generic when we don’t know beforehand which type we’re dealing with.
The type will be inferred later.
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
people: list[Person] = [] # a generic type (list) that infer that types are PersonIn our example above, we create a variable people that is a list. A list is a generic type since it can contain any data type we want.
In this case, we explicitly specified that our generic type, list, will be a list of Person. So when we iterate over our list, the type of each item will be inferred as Person:
# people: list[Person] = [] from example above
# person variable type will be Person
for person in people:
print(f"Name - {person.name}, Age - {person.age}")— What if we don’t know which data type to use?
Sometimes we don’t know the data type or it can be of any type.
people = [] # a empty list with that can be of any typeAnother way of doing this is by using the builtin TypeVar from the typing library:
from typing import TypeVar
T = TypeVar('T') # this meas T can be of any type
people: list[T] = [] # a empty list with that can be of any typeTypeVar allows us to
— Why TypeVar?
If you’re familiar with other programming languages like Java, you’ve probably seen something like this:
/**
* Generic version of the Dictionary class.
* @param <K> the key of the value for our dictionary
* @param <T> the type of the our dictionary
*/
public class Dictionary<K, T> { /* ... */ }In Java, as you can see we can create a representation of a generic type — T and K — without the need to explicitly define them.
The same was not possible in Python. Not since Python3.12.
Before Python3.12, if we wanted to define a generic type we had to explicitly create the type so it could be recognized inside our namespace.
from typing import TypeVar
T = TypeVar('T')
K = TypeVar('K')
class Dictionary:
def __init__(self, key: K, value: T):
passA generic type can also be bound or constrained to a certain data type if we wish:
from typing import TypeVar
T = TypeVar('T', bound=str) # any str or subtype of str object
K = TypeVar('K', str, int) # have to be a str or intWith Python3.12, now we don’t have to explicitly declare the TypeVar in our namespace — module —, we can take the same approach as we saw in the Java example:
class Dictionary[T, K]: # the new way of creating generic
def __init__(self, key: K, value: T):
passWe declare our generics after the class name adding a list with our expected parameters — generics.
This introduction brings lots of flexibility and freedom in how we create and handle generic types.
The same thing we did in a class, can be done with a function:
def get_value[K, T](key: K) -> T:
passHere we define a function that receives a key that can be of any type and returns a generic type as response — T.
If we want to create a bound or define a type, we can easily do that as well:
def get_value[K: (str, int), T: str](key: K) -> T:
passWith this, we are saying that our key — K, has to be of type str or int and our generic T can be of any kind of str or subtype of str object.
Now let’s jump into my favorite part — The type statement.
The type statement
If you want to define a new type in Python, let’s say a Dictionary like in our previous example, one of the ways to do it is by using a TypeAlias.
A TypeAlias is a type used to create aliases that serve as a ‘nickname’ to a fixed type that you create.
from typing import TypeAlias
Dictionary: TypeAlias = dict[str | int, str] # new type aliasWithout the TypeAlias Python would consider our line as a variable assignment.
With TypeAlias, we have a new type that we can use to define our variables:
items = []
def new_item(item: Dictionary) -> list[Dictionary]:
return items.append(item)So what does this have to do with the type statement you might ask?
Well, TypeAlias is deprecated in Python3.12. It’s not removed, but it's not encouraged either.
Introduced in Python3.12, we now have the type statement.
The type statement makes it cleaner and easier to spot a new type when is created.
The goal with this is to make it similar to how we create a new class using the class keyword or a function with the def keyword.
type Dictionary = dict[str | int, str] # new type aliasMuch better, if you ask me.
We can use a combination of generic types when creating our type alias and do all sorts of things. We’re only limited by our imagination and needs, of course.
type Dictionary = dict[K, T] # new type aliasBy the way, Python automatically understands that T and K are generic types. You can use any letter you want.
Let’s make a final test with everything we saw so far and see if nothing breaks:
type Dictionary = dict[K, T] # new type alias with generic types
items = []
def new_item(item: Dictionary) -> list[Dictionary]:
global items # access the global variable and append item to it
items.append(item)
new_item({'name': 'Yanick'})
new_item({1: 1})
print(items)
# the output should be - [{'name': 'Yanick'}, {1: 1}]Final thoughts
As I continue to delve into Python3.12 I continue to find new things that amaze me and make me want to explore more and share it with you guys here.
As I told you in my previous article, I had some issues with my computer and last week was hard since the problem was deeper than expected, some issues with Nvidia graphics I guess.
I couldn’t do much and the work accumulated so this article took longer than expected but we are finally here.
I hope this article brings value to you and helps you boost your Python code like it’s doing with mine.
See you soon enough.
Hi! Thank you for your time reading my article. If you enjoyed this article and would like to receive similar content directly to your inbox
In Plain English 🚀
Thank you for being a part of the In Plain English community! Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: X | LinkedIn | YouTube | Discord | Newsletter
- Visit our other platforms: Stackademic | CoFeed | Venture
- More content at PlainEnglish.io





