7 New Type Features in Python 3.13

The recently released Python 3.13 continues to push the limits of efficiency and elegance.

In addition to the much-discussed exciting free-threading model and Just-In-Time compiler, I am particularly drawn to the new improvements in the type system.

Building on the powerful type system introduced in earlier versions, Python 3.13 will introduce seven new type features that are expected to enhance code reliability and developer productivity.

In this article, we will try out these exciting new features and explore how they can simplify our code and elevate our programming practices to new heights.

All code snippets in this article have been tested on the latest release version of Python 3.13.0rc2, which is the final release preview of Python 3.13. The official release of 3.13.0 is scheduled for Tuesday, October 1, 2024.

1. ReadOnly Type

Define items as read-only

The new ReadOnly type, as the name suggests, is a special type construct used to mark items in a TypedDict as read-only.

from typing import TypedDict, ReadOnly

class Leader(TypedDict):
    name: ReadOnly[str]
    age: int

author: Leader = {'name': 'Yang Zhou', 'age': 30}
author['age'] = 31  # no problem to change
author['name'] = 'Yang'  # Type check error: "name" is read-only

The code above demonstrates its usage. Since we defined the name property as ReadOnly[str] type, changing its value will trigger a type inconsistency warning in the integrated development environment or other static type checking tools.

Note: The “ReadOnly” type can only be used in “TypedDict”.

If you prefer a simpler way to define TypedDict, you can also use the ReadOnly type:

from typing import TypedDict, ReadOnly

Leader = TypedDict("Leader", {"name": ReadOnly[str], "age": int})

author: Leader = {'name': 'Yang Zhou', 'age': 30}
author['age'] = 31  # no problem to change
author['name'] = 'Tim'  # Type check error: "name" is read-only

2. @warnings.deprecated

New decorator to indicate that an object has been deprecated

Good software continuously improves. This means not only adding new content but also removing outdated content.

However, directly removing functions or classes in the next new version is not user-friendly. We should not do this.

If you strictly adhere to Python’s official documentation, you will find that Python’s gradual removal strategy for obsolete objects is the industry standard:

  • Marking relative objects as deprecated, informing developers in advance about which objects will be removed in the future. But these objects can still be used in the next few versions.
  • After several versions, fully informed objects will be completely removed from Python’s latest version.

Python 3.13 provides us with a more convenient way to mark deprecated objects – a new decorator named @warnings.deprecated.

As long as an object is equipped with this decorator, static type checking tools or integrated development environments will remind us that we are using deprecated objects.

For example, I used a deprecated object in the program below, and the integrated development environment (PyCharm) reminds me with a noticeable strikethrough:

7 New Type Features in Python 3.13
A PyCharm screenshot to show how the deprecated decorator works

Simple and easy to use, this is yet another classic Pythonic design!

3. TypeIs

Making type narrowing easier

The new “TypeIs” concept aims at “type narrowing”, which is described in its official documentation as a technique used by static type checkers to determine the more precise type of expressions in the program code flow.

However, the likelihood of using it in our application code is not high. But we need to understand what it is:

In short, the form def foo(arg: TypeA) -> TypeIs[TypeB]: ... means that if foo(arg) returns True, then arg is an instance of TypeB, and if it returns False, then it is not an instance of TypeB.

4. is_protocol

A new function to quickly check if a class belongs to a protocol type

This new function is_protocol is a convenient way to check if an object is of Protocol type.

We simply import this function from the typing module and use it directly:

from typing import is_protocol, Protocol


class PersonProto(Protocol):
    name: str
    age: int

print(is_protocol(PersonProto))
# True
print(is_protocol(int))
# False

5. get_protocol_members

Function to return the set of protocol members

The new get_protocol_members() function is used to quickly obtain all item names of a Protocol type. It returns a frozenset containing all names:

from typing import Protocol, get_protocol_members


class PersonProto(Protocol):
    name: str
    age: int

print(get_protocol_members(PersonProto))
# frozenset({'age', 'name'})

6. Default types for TypeVar, ParamSpec, and TypeVarTuple

In Python 3.13, type parameters (typing.TypeVar, typing.ParamSpec, and typing.TypeVarTuple) now support default types, and their usage is very simple.

For example, the code below shows how to easily set a default type for TypeVar:

from typing import TypeVar
T = TypeVar("T", default=int)  # This means that if no type is specified T is int
print(T.has_default())
# True
S = TypeVar("S")
print(S.has_default())
# False

Python 3.13 also adds the has_default() function to check whether TypeVar has a default type.

7. NoDefault

Indicates no default value

In addition to providing default support for some new type parameters, the typing module also introduces a new object called NoDefault to indicate that a type parameter has no default value.

from typing import TypeVar, NoDefault
T = TypeVar("T")
print(T.__default__ is NoDefault)
# True

S = TypeVar("S", default=None)
print(S.__default__ is NoDefault)
# False

As the code above demonstrates, if no type is set as the default, then the TypeVar‘s __default__ property will be NoDefault. But if its default value is None, it still has a default value.

Performance Improvements and Method Removals

The official documentation for Python 3.13 mentions that by removing dependencies on re and contextlib, the import time for the typing module has been reduced by about one-third.

We also need to note that from this new version of Python, some typing-related items will be removed:

Removal of the typing.io and typing.re namespaces, which have been deprecated since Python 3.8. Items in these namespaces can be imported directly from the typing module.

Removal of the keyword argument method for creating TypedDict types, which has been deprecated since Python 3.11.

7 New Type Features in Python 3.13

Leave a Comment