Python 3.11 is a significant release of the programming language, bringing with it a number of new features and improvements. It’s faster overall than Python 3.10 on most benchmarks, uses less memory, and starts up faster. It also has a number of new and refined features that improve the developer experience, such as exception groups, exception notes, and fine-grained error locations in tracebacks.
Here’s a list of features that are introduced in Python 3.11:
Exception groups
Exception notes
The tomllib
library
Variadic generics
Marking TypedDict options
Self type
Arbitrary literal string type
Data class transforms
Exception groups are a new feature in Python 3.11 that allows us to group multiple exceptions and handle them collectively. This can be useful for a variety of reasons, such as:
To simplify error handling in complex code
To provide more context to users when errors occur
To handle multiple errors in a single handler
Exception notes are a way to document the different types of exceptions that a piece of code can raise. Other developers can use this information to understand the potential risks of using the code and write code that handles exceptions gracefully. Exception notes should be clear, concise, and informative. They should include the following information:
The name of the exception type
A description of the conditions under which the exception is raised
The recommended way to handle the exception
Some benefits of writing exception notes can be:
Helping other developers understand the potential risks of using our code
Helping developers write code that gracefully handles exceptions
Helping us debug our code
Helping make our code more robust and reliable
tomllib
libraryIn Python 3.10, there’s no built-in library to parse TOML
files, and we need third-party libraries, such as tomli
, to handle TOML
files. However, we don’t need to do that anymore because Python 3.11 introduced the tomllib
library that aids in parsing TOML
files. We can load and read TOML
data through the library but not write it. It’s based on the third-party tomli
library.
Note: TOML, which stands for Tom’s Obvious, Minimal Language, is an easily readable configuration file format because of its simple syntax. It was created to address some shortcomings of other configuration file formats.
The two main functions of the tomllib
library are:
load()
: It loads data from .toml
files.
loads()
: It loads data from str
.
Here’s an example of parsing TOML
files in Python 3.11:
import tomllibwith open('t.toml') as Sample:toml_data = tomllib.loads(Sample.read())print(toml_data)
In the code above:
Line 1: We import the tomllib
library.
Lines 3–4: Using with open()
, the code reads t.toml
, loads the content using the tomllib
library, and assigns parsed data to the variable toml_data
.
Line 6: The print(toml_data)
statement prints the parsed data.
Python 3.11 introduces a new feature called variadic generics. Variadic generics allow us to pass a variable number of type arguments to a generic function or class.
For example, the following generic function takes a variable number of type arguments:
def my_function(*args: Type[Any]) -> None:for arg in args:print(arg)my_function(int, str, float)
Here’s the explanation:
Line 1: This line defines a function called my_function()
with a parameter *args
that tells Python that the function can take a variable number of arguments. The Type[Any]
type hint tells Python that the arguments to the function must be of type Any
. This means that the function can be called with any type of argument and the my_function()
function doesn’t return
a value.
Lines 2–3: This line starts a for
loop that iterates over the args
parameter. For each argument, the function prints the argument to the console.
Line 5: This line calls the my_function()
function with three arguments: int
, str
, and float
.
Variadic generics can be very useful for writing generic code that can handle a variety of different types.
This feature allows us to mark certain options in a TypedDict as required or optional.
For example, the following TypeDict has two options: name
and age
:
my_type_dict: TypedDict = {"name": str,"age": Optional[int]}print(my_type_dict)
Here’s the explanation:
Line 1: This line declares a variable called my_type_dict
and assigns it to a TypedDict
object.
Lines 2–3: The TypedDict
object has two keys: name
and age
. The name
key has the type str
and the age
key has the type Optional[int]
. This means that the name
key must contain a string value and the age
key can contain an integer value or be None
.
Line 5: This line prints the my_type_dict
to the console.
Self
typeThe Self
type in Python 3.11 is a new type annotation that allows us to specify the return type of a method as the same type as the class in which the method is defined. This can be useful for improving the type safety of our code and making it more readable.
Here’s an example:
class Person:def __init__(self, name: str) -> None:self.name = namedef greet(self: Self) -> Self:print(f"My name is {self.name}")return selfperson = Person("Alice")
Here’s the explanation:
Line 1: This line defines a class called Person
.
Line 2: This line defines a constructor for the Person
class. The constructor takes a name
parameter of type str
.
Line 3: This line assigns the value of the name
parameter to the self.name
attribute.
Line 5: This line defines a method called greet()
. The greet()
method takes no parameters and returns an instance of the Person
class by using the Self
type hint.
Line 6: This line prints the message “My name is name
” to the console. The name
is replaced by the value given in the method parameter.
Line 7: This line returns an instance of the Person
class.
Line 9: This line creates a new instance of the Person
class and assigns it to the person
variable and adds the value of name
as the parameter.
Arbitrary literal string type is a new feature in Python 3.11 that allows us to specify the type of a variable as any literal string value. This can be useful for ensuring that variables only contain valid string values, and for making our code more readable.
To use the arbitrary literal string type, we can use the LiteralString
type hint. For example, the following code is valid:
my_variable: LiteralString = "Hello, world!"print(my_variable)
In the code above, the LiteralString
type hint is used, which indicates that my_variable
should have the exact value, "Hello, world!"
.
In Python 3.11, we can use the dataclasses
module to create data classes, which can simplify the creation of classes that primarily store data. These classes automatically generate special methods like __init__
, __eq__
, __ne__
, and __repr__
based on the class attributes. The dataclass_transform
decorator is used to mark a function, class, or metaclass as performing runtime “magic” that transforms a class, endowing it with dataclass-like behaviors.
Here’s an example of how to use dataclass–tranform
in Python 3.11:
import typingfrom typing import Type, TypeVar, Anyimport dataclasses_T = TypeVar("_T")@typing.dataclass_transform()def create_model(cls: Type[_T]) -> Type[_T]:cls = dataclasses.dataclass(cls) # Automatically create __init__, __eq__, and __ne__ methodsreturn cls# The create_model decorator can now be used to create new model classes:@create_modelclass CustomerModel:id: intname: strc = CustomerModel(327, "Eric Idle")print(c)
Here’s the explanation:
Line 1–3: Import all the necessary modules.
Line 5: Define the type variable named _T
. This is used to represent any type.
Line 7: Decorate the upcoming method with typing.dataclass_transform()
, which tells the type checker that the create_model()
method performs dataclass-like transformations.
Lines 8–10: Define the create_model()
method which takes a type object as input and returns a transformed object. It uses dataclasses.dataclass()
decorator to automatically create the __init__
, __eq__
, and __ne__
methods for the type object.
Line 13: Decorate the CustomerModel
class with the create_model()
decorator.
Lines 14–16: Define the CustomerModel
class which has two variables: id
and name
.
Line 18: Create an instance of the CustomerModel
class named c
and assign values to the variable fields.
Line 19: Print the values stored in c
.
A lot of features and functions were improved and modified in Python 3.11. Some of them are as follows:
Faster CPython
Improvements in the hashlib
module
Fine-grained error location in tracebacks
The -P
command-line option
Python 3.11 includes a number of optimizations that make the CPython interpreter faster. These optimizations include:
The new bytecode optimizer in Python 3.11 can generate more efficient code by performing a number of optimizations, including
The new garbage collector in Python 3.11 is more efficient and uses less memory by using a number of techniques, including
The new memory allocator in Python 3.11 is more efficient and reduces fragmentation by using a number of techniques, including
hashlib
moduleThe following improvements have been made to hashlib
modules in Python 3.11:
The hashlib.blake2b()
and hashlib.blake2s()
functions now prefer the libb2
library over the vendored copy. It improves performance significantly, especially on systems with a recent version of OpenSSL installed.
The hashlib
module now prefers optimized SHA-3 and SHAKE implementations from OpenSSL. This can also improve performance on systems with OpenSSL installed.
A new hashlib.file_digest()
function has been added. This function allows us to efficiently compute the hash of a file or file-like object. Here‘s an example of how to use the method:
import hashlibdef get_file_hash(filename, digest="sha256"):with open(filename, "rb") as f:return hashlib.file_digest(f, digest).hexdigest()file_hash = get_file_hash("myfile.txt")print(file_hash)
Here’s the explanation of the code:
Line 1: Import the hashlib
module.
Lines 3–5: Define a method get_file_hash()
with arguments: filename
and digest
. The default digest
algorithm is set to SHA256
.
Line 7: Compute the hash of myfile.txt
using the get_file_hash()
method and save the results in the file_hash
variable.
Line 8: Print the hash of the file
Python 3.11 introduced a new feature called fine-grained error location in tracebacks. This feature provides more detailed information about the location of errors in Python code.
For an example, run the following code:
def my_function(x):return x / 0my_function(1)
This output section shows the traceback that tells us that the error occurred on line 2 of the my_function()
function in the main.py
file.
-P
command-line option
The new -P
command-line option and PYTHONSAFEPATH
environment variable in Python 3.11 allow us to disable the automatic prepending of potentially unsafe paths to sys.path
. This can be useful for improving security and reducing the risk of arbitrary code execution.
Deprecated features and APIs are those that are no longer recommended for use and will be removed in future versions of Python. This can be done for a variety of reasons, such as security vulnerabilities, performance problems, or because the feature is no longer needed. Here’s a list of features/APIs that are deprecated in Python 3.11 and will be removed in later versions:
Py_UNICODE
encoder APIs have been converted to static inline functions, which are more efficient and easier to use.
A variety of Python APIs that have been deprecated for various reasons. For example, the pickle.Unpickler
class is deprecated because it’s vulnerable to security attacks.
It’s important to note that deprecated features and APIs are still functional in Python 3.11. However, it’s recommended to avoid using them if possible because they may be removed in future versions of Python.
Python 3.11 is a major release of the Python programming language, with a number of new features and improvements that make it faster, more reliable, and more user-friendly. Some of the most notable changes include faster performance, improved type safety, new language features, and an improved standard library.
It’s recommended to upgrade to Python 3.11. The upgrade process is relatively straightforward, and the benefits of using Python 3.11 are well worth it.
Free Resources