Python 3.12 Preview: Static Typing Improvements

Python 3.12 Preview: Static Typing Improvements
by:
blow post content copied from  Real Python
click here to view original post


Python’s support for static typing gradually improves with each new release of Python. The core features were in place in Python 3.5. Since then, there’ve been many tweaks and improvements to the type hinting system. This evolution continues in Python 3.12, which, in particular, simplifies the typing of generics.

In this tutorial, you’ll:

  • Use type variables in Python to annotate generic classes and functions
  • Explore the new syntax for type hinting type variables
  • Model inheritance with the new @override decorator
  • Annotate **kwargs more precisely with typed dictionaries

This won’t be an introduction to using type hints in Python. If you want to review the background that you’ll need for this tutorial, then have a look at Python Type Checking.

You’ll find many other new features, improvements, and optimizations in Python 3.12. The most relevant ones include the following:

Go ahead and check out what’s new in the changelog for more details on these and other features.

Recap Type Variable Syntax Before Python 3.12

Type variables have been a part of Python’s static typing system since its introduction in Python 3.5. PEP 484 introduced type hints to the language, and type variables and generics play an important role in that document. In this section, you’ll dive into how you’ve used type variables so far. This’ll give you the necessary background to appreciate the new syntax that you’ll learn about later.

A generic type is a type parametrized by another type. Typical examples include a list of integers and a tuple consisting of a float, a string, and another float. You use square brackets to parametrize generics in Python. You can write the two examples above as list[int] and tuple[float, str, float], respectively.

In addition to using built-in generic types, you can define your own generic classes. In the following example, you implement a generic queue based on deque in the standard library:

# generic_queue.py

from collections import deque
from typing import Generic, TypeVar

T = TypeVar("T")

class Queue(Generic[T]):
    def __init__(self) -> None:
        self.elements: deque[T] = deque()

    def push(self, element: T) -> None:
        self.elements.append(element)

    def pop(self) -> T:
        return self.elements.popleft()

This is a first-in, first-out (FIFO) queue. It represents the kind of lines that you’ll find yourself in at stores, where the first person into the queue is also the first one to leave the queue. Before looking closer at the code, and in particular at the type hints, play a little with the class:

>>>
>>> from generic_queue import Queue

>>> queue = Queue[int]()

>>> queue.push(3)
>>> queue.push(12)
>>> queue.elements
deque([3, 12])

>>> queue.pop()
3

You can use .push() to add elements to the queue and .pop() to remove elements from the queue. Note that when you called the Queue() constructor, you included [int]. This isn’t necessary, but it tells the type checker that you expect the queue to only contain integer elements.

Normally, using square brackets like you did in Queue[int]() isn’t valid Python syntax. You can use square brackets with Queue, however, because you defined Queue as a generic class by inheriting from Generic. How does the rest of your class use this int parameter?

To answer that question, you need to look at T, which is a type variable. A type variable is a special variable that can stand in for any type. However, during type checking, the type of T will be fixed.

In your Queue[int] example, T will be int in all annotations on the class. You could also instantiate Queue[str], where T would represent str everywhere. This would then be a queue with string elements.

If you look back at the source code of Queue, then you’ll see that .pop() returns an object of type T. In your special integer queue, the static type checker will make sure that .pop() returns an integer.

Speaking of static type checkers, how do you actually check the types in your code? Type annotations are mostly ignored during runtime. Instead, you need to install a separate type checker and run it explicitly on your code.

If you install Pyright, then you can use it to type check your code:

$ pyright generic_queue.py
0 errors, 0 warnings, 0 informations

To see an example of the kinds of errors that Pyright can detect, add the following lines to your generic queue implementation:

Read the full article at https://realpython.com/python312-typing/ »


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]


September 27, 2023 at 07:30PM
Click here for more details...

=============================
The original post is available in Real Python by
this post has been published as it is through automation. Automation script brings all the top bloggers post under a single umbrella.
The purpose of this blog, Follow the top Salesforce bloggers and collect all blogs in a single place through automation.
============================

Salesforce