The world’s leading publication for data science, AI, and ML professionals.

Python 3.10 – Five New Features And Considerations

Not only listing but also the examples and considerations.

Image by Joshua Woroniecki from Pixabay
Image by Joshua Woroniecki from Pixabay

A few days ago, Python 3.10 has released eventually. There are already many articles online that were even published before it was released. However, I found most of those are just listing the new features without too much discussion. Therefore, in my article, I’ll try to give some examples about how to use these new features. Also, I’ll discuss them with my personal opinions.

I won’t list these changes again. If you want a list of all new features, the official documentation is always the best place.

Python Release Python 3.10.0

Rather than just focusing on the features, I’ll also suggest how to get this new version of Python so we all can start to play around. Since it has already been released, I can use my favourite Python environments management toolkit – Anaconda to download this. If you haven’t got it yet, it can be downloaded for free.

Anaconda | Individual Edition

After installation, we can create a new Python virtual environment with the newest version – 3.10.

conda create --name py310 python=3.10

Congratulations! The latest Python has been installed.

1. Allow writing union types as X | Y

Image by Luisella Planeta Leoni from Pixabay
Image by Luisella Planeta Leoni from Pixabay

The first important feature is the union types syntax. Specifically, we can use a pipe in between two or more types to represent "either this type or that type".

The most common usage of this syntax would be checking the type of an object. Rather than writing multiple conditions like isinstance(x, int) or isinstance(x, str), we can now write the code as follows.

isinstance(x, int | str)

See the example below.

The first thing that comes to my mind is that we can use this syntax to write some robust conditions. For example, a variable should be an integer, but it also could be "None", which is also valid, then we can write the condition like this.

isinstance(x, int | None)

In addition to that is the function signature. Now, we can use this syntax to indicate a parameter can be of multiple types. For example, we want to write such a function to add 1 to the argument passed into it. The argument could be integer or string, and the returned value should be the same as the original type.

def plus_1(number: int | str):
    if isinstance(number, int):
        return number + 1
    else:
        number = int(number)
        return str(number + 1)

We can verify whether it works as expected.

2. Add Optional Length-Checking To zip()

Image by Stjepan Zlodi from Pixabay
Image by Stjepan Zlodi from Pixabay

The zip() function has been discussed deeply in one of my previous articles. This new feature in Python 3.10 was also mentioned here. It is a very powerful and useful function in Python. It is highly recommended to read my article if you don’t know too much about it.

"Fasten" Python Objects Using Zip

Briefly, the zip() function will get the items from two containers (such as a list) one by one and put the items at the corresponding positions at both containers together in a tuple. Have a look at the example below.

list(
    zip([1, 2, 3], ['a', 'b', 'c'])
)

By default, if we provided two lists with different lengths, the extra items in the longer one will be ignored. This is also known as the "Bucket Effects".

list(
    zip([1, 2, 3], ['a', 'b', 'c', 'd'])
)

In the above example, the item 'd' in the second list was ignored.

However, sometimes we may not aware that there are items ignored because the function will do that silently. Therefore, in Python 3.10, a new flag called "strict" is introduced to force the two lists to be of the same length.

list(
    zip([1, 2, 3], ['a', 'b', 'c', 'd'], strict=True)
)

If we explicitly set strict=True, an exception will be thrown when the lengths are different.

3. Parenthesized context managers are now officially allowed

Image by PublicDomainPictures from Pixabay
Image by PublicDomainPictures from Pixabay

In previous versions, having multiple clauses in one "with" statement is a bit painful. For example, if we want to open two files in a single "with" statement, we have to write them in a single line.

with open('sample_data/mnist_test.csv') as test, open('sample_data/mnist_train_small.csv') as train:
    pass

In the notebook, it looks like this.

This will be extremely painful when we have really long arguments in the open() function.

There is a solution to make our code better, which is adding a backward slash so that we can break them into multiple lines.

with open('sample_data/mnist_test.csv') as test, 
     open('sample_data/mnist_train_small.csv') as train:
    pass

It then looks like this.

It is definitely better, but still a bit ugly for that backward slash.

Now, in the new version, we can put the into a pair of parentheses so that the code will be neater and more readable.

with (open('1.txt') as first_file,
      open('2.txt') as second_file):

Personally, I would like to write the code as follows, which I think will be more readable, especially when we have more than two items.

with (
    open('1.txt') as first_file,
    open('2.txt') as second_file,
    open('3.txt') as third_file,
    open('4.txt') as fourth_file
):

4. The new syntax: match … case …

Image by Elmer Geissler from Pixabay
Image by Elmer Geissler from Pixabay

It is quite controversial to have the "switch … case …" syntax in Python. Even the father of Python, Guido, doesn’t support adding such syntax to Python. However, it is still released in this new version and I was actually convinced that this syntax is good.

Please be noticed that people usually say "switch case" because that’s commonly used in other Programming languages. However, in Python, the keywords are "match … case …".

For example, we can return different messages based on the HTTP code.

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 401:
            return "Unauthorized"
        case 403:
            return "Forbidden"
        case 404:
            return "Not found"
        case _:
            return "Unknown status code"

In this example, the conservative developers may still argue that it doesn’t actually provide any benefits to improve the code from readability. We can re-write this function using if-else and it is not too bad.

def http_error(status):
    if status == 400:
        return "Bad request"
    elif status == 401:
        return "Unauthorized"
    elif status == 403:
        return "Forbidden"
    elif status == 404:
        return "Not found"
    else:
        return "Unknown status code"

Indeed, for such a general use case, the match-case syntax didn’t make too much difference. However, in my opinion, the new keyword "match" indicated the core logic and purpose of this syntax. It is meant to make our life easier when we want to "match" some patterns for an expression.

For example, we want to write a function to return the host URL, the port number and the protocol type from a tuple. However, the tuple could have two items (host and port) only. In that case, we want to return a default protocol "https". If the tuple has already provided the protocol, we will need to return it.

Using if-else syntax, we have to write the code as follows.

def get_url(info):
    if isinstance(info, tuple) and len(info) == 2:
        host, port = info
        protocol = "http"
    elif isinstance(info, tuple) and len(info) == 3:
        host, port, protocol = info

    return host, port, protocol

It is actually not too bad, again. However, if we think about the two patterns that we are trying to match, we can re-write the code as follows using the new syntax.

def get_url(info):
    match info:
        case host, port:
            protocol = 'http'
        case host, port, protocol:
            pass

    return host, port, protocol

WoW! Very neat and clean, and the readability is much better than the if-else version. That’s what I think about how we should use this new syntax!

5. Enhanced error messages

Image by Andrew Martin from Pixabay
Image by Andrew Martin from Pixabay

Previously, the error message of Python is not ideal sometimes. For example, the code below has the right brace missing.

my_dict = {'name': 'Chris', 'age': 33

The error message will simply tell us that it wasn’t successful when parsing the code.

In the new version, these error messages are improved to a large extent. It will directly tell us why it failed to parse the code.

Some error message makes developers feel warm, too 😀

Summary

Image by yamabon from Pixabay
Image by yamabon from Pixabay

In this article, I have introduced the 5 most important new features in Python 3.10 which was released a few days ago. Hope it will be helpful and demonstrated these new features well with those examples I have provided.

Join Medium with my referral link – Christopher Tao

If you feel my articles are helpful, please consider joining Medium Membership to support me and thousands of other writers! (Click the link above)


Related Articles