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 optionThe 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