Мой взгляд на новые фичи python3.10-3.12

Стабильные минорные версии Python выходят ежегодно, последние 5 лет – в октябре. Мажорные версии, будем надеяться, выходить больше не будут, хватило ада переезда с python2 на python3. В этом октябре нас ожидает python3.13 (мажоная версия 3, минорная 13). Подумалось мне порефлексировать – какие новые фичи питона вошли в мой повседневный код. Пойдём со свежего и будем погружаться в пучины истории, в этой статье дойдём до 3.10. Запускаться будем в докере на python:3.12.5-slim. Код всех примеров лежит тут.

Пример 1: python3.12 – f-строки

В 3.12 меня зацепило обновление f-строк в PEP701. Вообще за много итераций f-строки превратились в мега-удобную штуку. Теперь внутри можно почти что угодно, в том числе вложенные вызовы f-строк с кавычками. Выведем в f-строке значение словаря по ключу:

print("Example 1. Python3.12, PEP701 https://peps.python.org/pep-0701/") settings = { "color": "green", "font": 14 } print(f"Settings: {settings["color"]}")

В выводе будет значение ключа:

# при запуске в python3.12 Settings: green

Для старых версий покажу, какая возникает ошибка. Полезно для "насмотренности", когда вы визуально будете отличать, это баг в коде или кто-то использует старый интерпретатор. Раньше так было нельзя, python3.11 и ранее выдаёт ошибку

# при запуске в python3.11 и ранее print(f"Settings: {settings["color"]}") ^^^^^ SyntaxError: f-string: unmatched '['

Можно вставлять многострочные f-строки и внутри указывать любые валидные python-выражения. Красивое. Это как в 3.8 добавили мега-удобную мелочь: с помощью знака равно после переменной в f-строке мы получаем вывод в формате "название_переменной=значение". Типа

user='Gvido' >>> print(f"{user=}") # вывод user='Gvido'

Пример 2: python3.11 – дополнение к исключениям

У исключений теперь есть метод add_note, с помощью которого можно дополнять порождённое исключение дополнительной информацией. Раньше нужно было либо отдельно логгировать нужное, либо колдовать над классом исключений. Укажем время возникновения исключения:

print("Example 2. Python3.11, PEP678 https://peps.python.org/pep-0678/") import datetime try: 1/0 except ZeroDivisionError as err: err.add_note(f"Except at {datetime.datetime.now()}") raise

В 3.11 дополнение (note) будет выведена после самого исключения. Выглядит так:

# при запуске в python3.11 File "/app/examples.py", line 17, in main 1/0 ~^~ ZeroDivisionError: division by zero Except at 2024-08-25 11:14:18.920230

До 3.11 метода add_note не существовало:

# при запуске в python3.10 и ранее err.add_note(f"Except at {datetime.datetime.now()}") AttributeError: 'ZeroDivisionError' object has no attribute 'add_note'

В 3.11 также ввели Exception Groups PEP654, но мне синтаксис не очень зашёл. Вы используете?

Пример 3: python3.10 – объединение контекстных менеджеров

Починили объединение контекстных менеджеров (parenthesized context managers). Как я понял, в теории так можно было изначально, на практике это был баг. Под одним with теперь можно собирать множество сущностей:

print("Example 3. Python3.10, bugfix") with (open("/tmp/1", "w+") as file1, open("/tmp/2", "w+") as file2): print(file1, file2)

В 3.10 работает

# при запуске в python3.10 <_io.TextIOWrapper name='/tmp/1' mode='w+' encoding='UTF-8'> <_io.TextIOWrapper name='/tmp/2' mode='w+' encoding='UTF-8'>

А в 3.9 и раньше жалуется на рандомную часть выражения

# при запуске в python3.9 и ранее with (open("/tmp/1", "w+") as file1, ^ SyntaxError: invalid syntax

Пример 4: python3.10 – pattern matching

Основным нововведением 3.10 считается введение Structural Pattern Matching. В питоне изначально не было switch/case, и вопрос решался либо цепочками elif, либо с помощью словаря. Пример такого словаря можете посмотреть в полном коде к этой статье внизу. Попытка внедрить switch/case была ещё в 2006 году, но тогда решили не вводить. В 2020 году Гвидо ван Россум презентовал новую реализацию, и ещё какую. В case запихнули сразу регулярки. Встречайте: PEP636. Ну, технически 634 вводит этот функционал, а в 636 предлагается туториал. Что теперь можно сделать? Представим, что мы парсим команду, состоящую из действия и объекта, вроде "нажать кнопка"

print("Example 4. Python3.10, PEP636 https://peps.python.org/pep-0636/") command = input("Write action and object (example 'push button'): ") match command.split(): case [action]: # interpret single-verb action print(f"You did '{action}'") case [action, obj]: # interpret action, obj print(f"You did '{action}' on '{obj}'")

При запуске в 3.10 после ввода двух слов получаем верный ответ

# при запуске в python3.10 Example 4. Python3.10, PEP636 https://peps.python.org/pep-0636/ Write action and object (example 'push button'): push button You did 'push' on 'button'

На более старых версиях match не известно

# при запуске в python3.9 и ранее match command.split(): ^ SyntaxError: invalid syntax

В case можно фиксировать какие-то блоки, использовать логическое "или" и переменные

# обработчик для направления или варианта go направление case ["north"] | ["go", "north"]: # обработчик для разных вариантов получения объекта get, pick up, pick ... up, при этом сам объект попадёт в переменную obj case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]:

И в этом match/case ещё много разных вариантов и нюансов. Это и хорошо (потому что мощь), и плохо (потому что разбирать чужой код может быть больно). Про детали у нас в канале был отличный пост.

Какие фичи показались важными вам? Я намеренно опустил всё про типы, это заслуживает отдельной статьи.

Весь проект "на потыкать" лежит тут.

В телеграм-канале DevFM пишу о полезном для разработчика: инструментах вроде parabol или fcron, интересных хаках вроде запуска LLM прямо в шрифте, оптимизаторе join в PostgreSQL. А ещё у нас есть бесплатный курс cli-for-dev по Linux на степике, немного подкастов и видео.

11
1 комментарий

Комментарий удалён модератором

Автор

Угу. Поэтому неплохо бы весь код паковать в докер и не зависеть от интерпретатора питона на хосте