Стабильные минорные версии Python выходят ежегодно, последние 5 лет – в октябре. Мажорные версии, будем надеяться, выходить больше не будут, хватило ада переезда с python2 на python3. В этом октябре нас ожидает python3.13 (мажоная версия 3, минорная 13). Подумалось мне порефлексировать – какие новые фичи питона вошли в мой повседневный код. Пойдём со свежего и будем погружаться в пучины истории, в этой статье дойдём до 3.10. Запускаться будем в докере на python:3.12.5-slim. Код всех примеров лежит тут.
В 3.12 меня зацепило обновление f-строк в PEP701. Вообще за много итераций f-строки превратились в мега-удобную штуку. Теперь внутри можно почти что угодно, в том числе вложенные вызовы f-строк с кавычками. Выведем в f-строке значение словаря по ключу:
Пример 1. f-строки на стероидах
В выводе будет значение ключа:
# при запуске в 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, с помощью которого можно дополнять порождённое исключение дополнительной информацией. Раньше нужно было либо отдельно логгировать нужное, либо колдовать над классом исключений. Укажем время возникновения исключения:
Пример 2. Дополняем исключения информацией
В 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 теперь можно собирать множество сущностей:
Пример 3. Объединение внутри with
# при запуске в 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 предлагается туториал. Что теперь можно сделать? Представим, что мы парсим команду, состоящую из действия и объекта, вроде "нажать кнопка"
При запуске в 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 на степике, немного подкастов и видео.