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).
brew update
brew upgrade pyenv
pyenv install 3.10.0
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:
Animal
I like this new syntax just because it is cleaner and might also be more useful.
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"), ...:
...
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
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
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()
Those are a little less interesting so feel free to check it by yourself:
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.