rogulski.it

My name is Piotr, a passionate pythonista and this is my blog!

    Checkout what's new in Python 3.10

    Posted at — Oct 4, 2021

    Today is the day! Python 3.10.0 was just released (4 October 2021) introducing new cool features and improvements. This post shows a short overview with code examples of most interesting PEPs (by me).

    OS X installation

    brew update
    brew upgrade pyenv
    pyenv install 3.10.0
    

    Structural Pattern Matching

    The long-awaited in Python world match/case statement, introducing a new way of checking the match of variable in terms of value and type. This was possible previously by introducing a couple of different approaches including dictionaries, ifs and isinstance/issubclass functions.

    Let’s make an example of a match pattern in Python for an animal price list for our farm (we can buy only in a given count of animals matches):

    from dataclasses import dataclass
    from typing import Type
    
    @dataclass()
    class Animal:
        count: int = 0
    
    
    class Cow(Animal):
        ...
    
    
    class Chicken(Animal):
        ...
    
    
    def check_purchase_list(animal: Type[Animal]) -> str:
        match animal:
            case Cow(count=10):
                return "Not buy"
            case Cow(count=20):
                return "Buy"
            case Chicken(count=10):
                return "Buy"
            case Checking(count=20):
                return "Not buy"
            case _:
                return "Not buy"
    
    
    check_purchase_list(Cow(10))
    # 'Not buy'
    
    check_purchase_list(Cow(20))
    # 'Buy'
    
    check_purchase_list(Chicken(10))
    # 'Buy'
    

    To implement the above example before we would need to:

    1. Check if an animal is a subclass of Animal
    2. Check if the count is equal to the given number
    3. Check if other conditions are not sufficient

    I like this new syntax just because it is cleaner and might also be more useful.

    Parenthesized context managers

    Are you feeling tired of getting millions of indentations in tests because of mock context managers? Not any more!

    Now, you can use officially a new syntax which allows you to attach multiple context managers under one with statement in one parentheses

    The most common usage in my codebases in real life is a test case:

    def test_my_amazing_api():
        with (
            mock_service1() as service1,
            mock_service2() as service2
        ):
            response1 = service1.get(...)
            response2 = service2.get(...)
    

    Simple, but joyful, again -> nothing that could not be done before on Python < 3.9:

        with mock_service1() as service1:
            with mock_service2() as service2:
                response1 = service1.get(...)
                response2 = service2.get(...)
        
        # or everything it the same line:
        with open("open1.txt") as open1, open("open2.txt") as open2, open("open3.txt"), ...:
            ...
    

    Precise line numbers for debugging and other tools

    More detailed error messages during the code debugging:

    FIRST_LINE = 1
    
    my_list  = ["this is", "my perfect list",
    
    print(my_list)
    

    Error on Python 3.10:

      File "//test.py", line 3
        my_list  = ["this is", "my perfect list",
                   ^
    SyntaxError: '[' was never closed
    

    Error on Python 3.9:

      File "//test.py", line 7
    
        ^
    SyntaxError: unexpected EOF while parsing
    

    New typing features

    Allow writing union types as X | Y

    You don’t need to import Union any longer from typing module.

    All we can do is to use | operator.

    Example on Python 3.10:

    
    def check(check_id: str | int) -> int | str:
        return check_id
    

    Example on Python < 3.10:

    from typing import Union
    
    def check(check_id: Union[str, int]) -> Union[str, int]:
        return check_id
    

    Explicit Type Aliases

    User-specific aliases are now possible with PEP 612, you can now define your type at the top of the module and use it, even if the assigned type is not defined yet.

    Example on Python 3.10:

    MyType: TypeAlias = “ClassName”
    def foo() -> MyType:
        ...
    
    
    class ClassName:
        def __init__(self) -> None:
            instance = foo()
    

    Important deprecations, removals or restrictions

    Those are a little less interesting so feel free to check it by yourself:

    Summary

    Migration from Python 2 to 3.x was a major change for many companies (and for some of them it still is), but this change is for good. In my current job, we are always trying to use the latest Python version. Seeing every new version, checking them out, and most important using them in code for millions of users I can say that Python is going in the right direction.