How Python 3.12 Simplifies Generics with New PEP‑695 Syntax
Python 3.12 introduces PEP‑695, a new generic syntax that streamlines type annotations by allowing direct use of type parameters without extra imports, enabling clearer, more concise code for functions and classes, and aligning Python’s typing capabilities with modern programming languages.
Python is a dynamically typed programming language, meaning variable types are determined at runtime, which makes it flexible and easy to use without explicit type declarations.
Although generic types and type parameters are gaining popularity, the syntax for defining type parameters remains tightly coupled to Python’s specific structures, causing confusion for developers.
The Python static‑typing community agrees that it is time to introduce a formal generic syntax similar to that of other modern languages.
Python 3.12 (PEP‑695) introduces a new generic syntax that simplifies type definitions and reduces boilerplate. The new syntax lets us use type parameters, improving readability and reducing errors as projects grow.
First, let’s see how to add type annotations. Type annotations let us specify function parameter and return types, improving readability and maintainability. For example:
<code>def add(a: int, b: int) -> int:
return a + b</code>In this example, the add function accepts two int parameters and returns an int value, providing clear expectations for developers and static analysis tools.
Next, we look at a simple class that uses the add method to add two integers:
<code>class Adder:
def add(self, a: int, b: int) -> int:
return a + b
adder = Adder()
c: int = adder.add(1, 2)</code>The Adder class contains an add method that takes two int arguments and returns their sum. An instance of Adder is created and the result is stored in the int variable c .
Currently the class only handles integers. To support various data types we can use generics. PEP‑646 provides the ability to create generic types with typing.TypeVar .
Now we can convert the Adder class into a generic class that works with different types:
<code>from typing import TypeVar, Generic
T = TypeVar('AdderType')
class Adder(Generic[T]):
def add(self, a: T, b: T) -> T:
return a + b
adder = Adder[str]()
c: str = adder.add("a", "b")
adder = Adder[int]()
c: int = adder.add(1, 1)</code>With Python 3.12’s new syntax, using generics becomes even easier; we no longer need to import Generic and TypeVar because the syntax handles them directly. Example:
<code>class Adder[T]:
def add(self, a: T, b: T) -> T:
return a + b
adder = Adder[str]()
c: str = adder.add("a", "b")
adder = Adder[int]()
c: int = adder.add(1, 1)</code>As shown, the new syntax makes code simpler and more intuitive, eliminating the need to import Generic and TypeVar and allowing generics to be used directly in classes and methods.
Previously, declaring type aliases required the typing module:
<code>from typing import TypeAlias, TypeVar
_T = TypeVar("_T")
ListOrSet: TypeAlias = list[_T] | set[_T]
</code>Now, with Python 3.12, the same can be written as:
<code>type ListOrSet[T] = list[T] | set[T]
</code>Python 3.12 brings significant improvements to type handling and generics, offering a more concise and intuitive syntax that requires no extra imports, simplifying development and aligning Python with other modern languages that support similar features.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.