What's New in Python 3.10 – The Most Important Features and Changes

Python has long held key positions on the most popular programming languages list. It currently ranks first in the TIOBE Index for July 2022 & PYPL Popularity of Programming Language, ahead of Java, JS, and C/C++. This article will discuss the most important new features in Python 3.10 that have appeared in recent years and why you should switch to them.

Changes in Python 3.8 vs 3.7

Release Date: October 2019

Changes in Python 3.8 vs 3.7

Self-documenting expressions and debugging for f-strings

We will recall how with Python 3.6, users got an excellent opportunity to format strings using the .format() method and the f prefix before the line and {} to place data inside the expression. Often we needed to name what we were deriving to add specifics.

Example Python <3.8

Since version 3.8, users can create lines for self-documenting expressions and debugging. It has become even easier to do this. We add the symbol `=` inside the {} and get the same result.

Example Python 3.8 feature

New walrus `:=` operator

We can directly perform two actions in a condition, a loop, or something else: assign a value to a variable and then compare the expression. Let's give an example of a simple console application that displays everything the user enters, as long as the line is not empty.

Example Python <3.8

Here we assign the result of user input twice: before the loop and in its body. Now we have a way to immediately save the outcome of the execution when checking the condition:

Example Python 3.8 feature

As you can see, we follow the same steps: first, we assign a value to the user_input variable, then perform the validation. If it works, we go into the loop and output the updated content of user_input until the user input is empty.

What about using the user_input variable later in the code, after the loop? Let's explore a situation where we create two variables using the walrus operator:

Example Python 3.8 feature

Let's try to output the value outside the conditional statement:

Example Python 3.8 feature

However, you should notice that Python will reject any of those values and check if the first operand is False. So, `n` will always be created, but `first_company` will only be made when the expression `n == 1` or the next `n > 1` is executed.

Example Python 3.8 feature

As we can see, n also exists after the body of the conditional statement if, when first_company was not initialized, so we got the NameError: name 'first_company' is not defined.

This approach makes it possible to create more concise designs, but the main thing is not to mess around too much. 😀

Ability to specify positional-only arguments

Here is an explanation from the official documentation:

Now users can limit parameters that must pass positionally and cannot be passed as a keyword, positional, positional or keyword, only keyword, to some functions.

Example Python 3.8 feature

Let's explore an example of built-in functions where we should not transfer keywords but only positional parameters.

Example Python 3.8 feature

We cannot pass keywords 'a, b' arguments to the divmod function. Likewise with len(), where we pass only positional arguments:

Example Python 3.8 feature

You might ask: how to transfer args/kwargs in this case? Well, here's the solution:

Example Python 3.8 feature

DictReader returns a simple dict instead of an OrderedDict

With Python 3.7, we got guaranteed ordered dicts, which covered all the possible advantages of collections.OrderedDict used to have over dict. So we got a logical change of return result in the csv module. If previously DictReader necessarily returned a list of OrderedDicts:

Example Python <3.8

Now, since Python 3.7 no longer makes sense, the result is currently not an OrderedDict, but a simple dict.

Example Python 3.8 feature

Changes in Python 3.9 vs 3.8

Release Date: October 2020

Changes in Python 3.9 vs 3.8

Ability to use | (ior) from dict

Using | for dict! Now we have the possibility to use | (ior) also with dict. Merging when using |, and updating when using |= as an assignment operator.

Example Python 3.9 feature

And

Example Python 3.9 feature

Type hinting using built-in collections types

Now you don't need to import the typing module to add typing to lists, dicts, sets, and other built-in classes.

Example Python 3.9 feature

.removeprefix() and .removesuffix() will override .startswith() or .endswith()

Are you using the .startswith() or .endswith() string methods to match and remove? Users now can do this more easily with the new .removeprefix() and .removesuffix() methods.

Example Python 3.9 feature

Changes in Python 3.10 vs 3.9

Release Date: October 2021

Changes in Python 3.10 vs 3.9

Using the new Union `|` operator

In Python 3.9, we looked at adding built-in collections to type hinting. Among the new features in Python 3.10, we received another improvement - the use of the new Union operator `|`. If previously we were forced to specify all types in Union:

Example Python <3.10

Now we specify the types, breaking one with the other operator '|':

Example Python 3.10 feature

The same applies to Optional because it is a Union[Type, None].

Example Python <3.10

Example Python 3.10 feature

It may be noted that mypy supports PEP-604.

Ability to use context managers with parentheses

The change will help to split several collections that users pass to the context manager into several lines:

Example Python 3.10 feature

Now it looks a lot nicer than single-line iterating across commas. Note that the trailing comma is supported.

Structural pattern matching

We will not discuss the historical component; let's just say that this Python 3.10 breaking change came to us from many other languages, for example, Haskell, Scala, Rust and so on. We need to immediately note that pattern matching should not be confused with a simple Match Case because it is not just a switch statement like in C++. Pattern matching here acts as a verification mechanism and helps with data unpacking and managing the execution flow.

Syntax:

Example Python 3.10 feature

It is often necessary to build structures like this one:

Example Python <3.10

Someone might say that it is possible to write it shorter, for example by separating the list of beginners into a list/tuple - OK:

Example Python <3.10

However, now we can do it more easily:

Example Python 3.10 feature

As you can see, we have the option to use | ("or"), and instead of "default", we use _ (wildcard). It will work if all other cases above do not work. Note that after this block, we cannot use other cases. Otherwise, we will receive a SyntaxError: wildcard makes remaining patterns unreachable error.

Example Python 3.10 feature

Will they continue to write like this in the future? We will find out in the coming years because whether they choose to use old or new records will ultimately depend on the users.

But wait, there's more! 😀

Sometimes, users not only check for equality for some reason but want to check whether the input data is a list or a tuple. In this case, the annotation is not enough. It only assists with static analysis through linters, development environments, or mypy. Anything can still get into the input of such a function. After successful validation, we want to extract and use the value in the body of the condition:

Example Python <3.10

This is how we did checking and unpacking in previous versions, but why leave it this way if pattern matching allows you to do two things at once:

Example Python 3.10 feature

Elegant, isn't it? You can think of many use cases; one of them could be using pattern matching with classes. Let's explore an example.

Let's describe the classes, Vector2D and Vector3D:

Example Python 3.10 feature

Let's create a function that accepts an instance of the Vector2D or Vector3D class as a matcher and displays information about them otherwise, we will display "Another vector":

Example Python 3.10 feature

As you can see, the entries are very similar to calling Vector2D or Vector3D constructors, but you need to understand that the matcher does not call constructors. Verification takes place by comparing the parameters. We can even check exclusively for x=0 when creating such an instance would result in a TypeError:

Example Python 3.10 feature

We will leave you the opportunity to experiment and find your ways of using this new Python 3.10 feature. However, we are sure that this operator will be frequently used due to its conciseness and multifunctionality, provided it is used correctly.

Key takeaways

Modern market dynamics require constantly updating knowledge and quickly responding to new trends. We are confident that new functionality in Python 3.10 will update the best-practice list soon, which means that you, as a specialist, will be able to speed up the development and maintenance of your code. So, if you haven’t read what’s new in Python 3.11, we’d recommend you do that, as there we’re talking about moving from Python 3.10 to Python 3.11.

Thanks for reading. Glory to Ukraine 🇺🇦