Все важные фичи и изменения в Python 3.10

от автора

Если вам хочется попробовать все фичи великолепной последний версии Python, нужно установить альфа или бета-версию. Однако учитывая, что эти версии не стабильны, мы не хотим перезаписывать дефолтную установку python. Будем устанавливать альфу Python 3.10 рядом с текущим интерпретатором.


Сделать это можно выполнив эти команды:

wget https://www.python.org/ftp/python/3.10.0/Python-3.10.0a6.tgz tar xzvf Python-3.10.0a6.tgz cd Python-3.10.0a6 ./configure --prefix=$HOME/python-3.10.0a6 make make install $HOME/python-3.10.0a6/bin/python3.10

После запуска кода выше вы увидите приветствие от среды разработки IDLE:

Python 3.10.0a6 (default, Mar 27 2021, 11:50:33) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>

А теперь, с установленным Python, мы можем посмотреть на все новые фичи и изменения.

Улучшения в проверке типов

Если вы пользуетесь проверкой типов, то будете счастливы услышать, что Python 3.10 включает много улучшений в проверке типов, среди них оператор объединения типов, синтаксис которого теперь чище.

# Function that accepts either `int` or `float` # Old: def func(value: Union[int, float]) -> Union[int, float]:     return value  # New: def func(value: int | float) -> int | float:     return value

Кроме того, это простое улучшение не ограничивается только аннотациями типа, оно может применяться с функциями isinstance() и issubclass():

isinstance("hello", int | str) # True

Изменения синтаксиса псевдонима типа

В более ранних версиях Python добавлены псевдонимы типов, позволяющие создавать синонимы пользовательских классов. В Python 3.9 и более ранних версиях псевдонимы записывались так:

FileName = str  def parse(file: FileName) -> None:     ...

Здесь FileName — псевдоним базового типа строки Python. Однако, начиная с Python 3.10, синтаксис определения псевдонимов типов будет изменён:

FileName: TypeAlias = str  def parse(file: FileName) -> None:     ...

Благодаря этому простому изменению и программистам, и инструментам проверки типа проще отличить присваивание переменной от псевдонима. Новый синтаксис обратно совместим, так что вам не нужно обновлять старый код с псевдонимами.

Кроме этих 2 изменений появилось другое улучшение модуля typing — в предложениях по улучшению номер 612 оно называется Parameter Specification Variables. Однако это не то, что вы найдете в основной массе кода на Python, поскольку эта функциональность используется для пересылки параметра типов от одного вызываемого типа к другому вызываемому типу, например, в декораторах. Если вам есть где применить эту функциональность, посмотрите её PEP.

bit_count()

Начиная с Python 3.10, чтобы посчитать количество битов в двоичном представлении целого числа, можно вызвать int.bit_count(). Функция известна как Population Count (popcount):

value = 42 print(bin(value)) # '0b101010' print(value.bit_count()) # 3

Это, безусловно, хорошо, но давайте будем реалистами: реализация этой функции не так уж сложна, на самом деле это всего лишь одна строка:

def bit_count(value):     return bin(value).count("1")

При этом popcount() — ещё одна удобная функция, она может пригодиться в какой-то момент; всевозможные полезные маленькие функции — одна из причин, по которой Python так популярен: на первый взгляд всё доступно из коробки.

Модуль distutils устарел

В новой версии функции не только добавляются, но также удаляются или объявляются устаревшими. Это касается пакета distutils, который объявлен устаревшим в 3.10 и будет удален в 3.12. На какое-то время его заменили пакетами setuptools и packaging, поэтому если вы работаете с каким-то из этих пакетов, у вас все будет хорошо. При этом вы, вероятно, должны проверить, не используется ли distutils в вашем коде, и начать готовиться к тому, чтобы в ближайшее время избавиться от этого модуля.

Синтаксис менеджера контекста

Контекстные менеджеры отлично подходят, чтобы открывать и закрывать файлы, работать с соединениями баз данных и делать многое другое, а в Python 3.10 они станут немного удобнее. Изменение позволяет в скобках указывать несколько контекстных менеджеров, что удобно, если вы хотите создать в одном операторе with несколько менеджеров:

with (     open("somefile.txt") as some_file,     open("otherfile.txt") as other_file, ):     ...  from contextlib import redirect_stdout  with (open("somefile.txt", "w") as some_file,       redirect_stdout(some_file)):     ...

В коде выше видно, что мы даже можем ссылаться на переменную, созданную одним контекстным менеджером (… as some_file) в следующем за ним менеджере!

Это всего лишь два из многих новых форматов в Python 3.10. Улучшенный синтаксис довольно гибок, поэтому я не буду утруждать себя и показать все возможные варианты; я почти уверен, что новый Python обработает всё, что вы ему скормите.

Улучшения в производительности

Как и во всех последних релизах Python, с Python 3.10 придут улучшения производительности. Первое — оптимизация конструкторов str(), bytes() и bytearray(), которые должны стать примерно на 30% быстрее (фрагмент, адаптированный из примера в баг-трекере Python):

~ $ ./python3.10 -m pyperf timeit -q --compare-to=python "str()" Mean +- std dev: [python] 81.9 ns +- 4.5 ns -> [python3.10] 60.0 ns +- 1.9 ns: 1.36x faster (-27%) ~ $ ./python3.10 -m pyperf timeit -q --compare-to=python "bytes()" Mean +- std dev: [python] 85.1 ns +- 2.2 ns -> [python3.10] 60.2 ns +- 2.3 ns: 1.41x faster (-29%) ~ $ ./python3.10 -m pyperf timeit -q --compare-to=python "bytearray()" Mean +- std dev: [python] 93.5 ns +- 2.1 ns -> [python3.10] 73.1 ns +- 1.8 ns: 1.28x faster (-22%)

Другой более заметной оптимизацией (если вы используете аннотации типов) является то, что параметры функции и их аннотации вычисляются уже не во время исполнения, а во время компиляции. Теперь функция с аннотациями параметров создаётся примерно в два раза быстрее.

Кроме того, есть еще несколько оптимизаций в разных частях ядра языка. Подробности о них вы можете найти в этих записях баг-трекера Python: bpo-41718, bpo-42927 и bpo-43452.

Сопоставление шаблонов

Одна масштабная фича, о которой вы, конечно, слышали, — это структурное сопоставление шаблонов, добавляющее оператор известное выражение case из других языков. Мы знаем, как работать с case, но посмотрите на вариацию в Python это не просто switch/case, но также несколько мощных особенностей, которые мы должны исследовать.

Простое сопоставление шаблонов состоит из ключевого слова match, за которым следует выражение, а его результат проверяется на соответствие шаблонам, указанным в последовательных операторах case:

def func(day):     match day:         case "Monday":             return "Here we go again..."         case "Friday":             return "Happy Friday!"         case "Saturday" | "Sunday":  # Multiple literals can be combined with `|`             return "Yay, weekend!"         case _:             return "Just another day..."

В этом простом примере мы воспользовались переменной day как выражением, которое затем сравнивается с конкретными строками в case. Кроме строк, вы также можете заметить case с маской _ — это эквивалент ключевого слова default в других языках. Хотя этот оператор можно опустить, в этом случае может произойти no-op, по существу это означает, что вернётся None.

Еще один момент, на который стоит обратить внимание в коде выше, это оператор |, позволяющий комбинировать несколько литералов | (другой его вариант — or).

Как я уже упоминал, новое сопоставление шаблонов не заканчивается на базовом синтаксисе, напротив — оно привносит дополнительные возможности, например сопоставление сложных шаблонов:

def func(person):  # person = (name, age, gender)     match person:         case (name, _, "male"):             print(f"{name} is man.")         case (name, _, "female"):             print(f"{name} is woman.")         case (name, age, gender):             print(f"{name} is {age} old.")          func(("John", 25, "male")) # John is man.

Во фрагменте выше мы воспользовались кортежем как выражением сопоставления. Однако мы не ограничены кортежами: работать будет любой итерируемый тип. Также выше видно, что маска (wildcard) _ может применяться внутри сложных шаблонов и не только сама по себе, как в предыдущих примерах. Простые кортежи или списки — не всегда лучший подход, поэтому, если вы предпочитаете классы, код можно переписать так:

from dataclasses import dataclass  @dataclass class Person:     name: str     age: int     gender: str      def func(person):  # person is instance of `Person` class     match person:         # This is not a constructor         case Person(name, age, gender) if age < 18:  # guard for extra filtering             print(f"{name} is a child.")         case Person(name=name, age=_, gender="male"):  # Wildcard ("throwaway" variable) can be used             print(f"{name} is man.")         case Person(name=name, age=_, gender="female"):             print(f"{name} is woman.")         case Person(name, age, gender):  # Positional arguments work             print(f"{name} is {age} years old.")  func(Person("Lucy", 30, "female")) # Lucy is woman. func(Person("Ben", 15, "male")) # Ben is a child.

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

Выше мы можем увидеть другие особенности сопоставления шаблонов: во-первых выражение в case — это гард, который также является условием в if. Это полезно, когда сопоставления по значению не достаточно и вам нужны дополнительные проверки. Посмотрите на оставшееся выражение case: видно, что и ключевые слова, (name-name) и позиционные аргументы работают с синтаксисом, похожим на синтаксис конструкторов; то же самое верно для маски _ (или отбрасываемой переменной).

Сопоставление шаблонов также позволяет работать с вложенными шаблонами. Вложенные шаблоны могут использовать любой итерируемый тип: и конструируемый объект, и несколько таких объектов, которые возможно итерировать:

match users:     case [Person(...)]:         print("One user provided...")     case [Person(...), Person(...) as second]:  # `as var` can be used to capture subpattern         print(f"There's another user: {second}")     case [Person(...), Person(...), *rest]:  # `*var` can be used as unpacking         print(...)

В таких сложных шаблонах для дальнейшей обработки может быть полезно записать подшаблон в переменную. Это можно сделать с помощью ключевого слова as, как показано выше, во втором case.

Наконец, оператор * может использоваться для «распаковки» переменных в шаблоне, это работает и с маской _ в шаблоне *_. Если вы хотите увидеть больше примеров и законченный туториал, пройдите по ссылке на PEP 636.

Заключение

В Python 3.10 появилось много интересных новых возможностей, но этот релиз — alpha (вскоре появится beta) всё ещё далек от полноценного тестирования и готовности к производственной среде. Поэтому определенно не стоит начинать использовать его.

Наверное, лучше подождать полноценного релиза в октябре и, возможно, время от времени проверять страницу Что нового в Python 3.10, чтобы увидеть изменения, которые могут появиться на последних минутах.

С учётом сказанного выше — если вы хотите обновиться — неплохой идеей может быть взять на тест первый релиз beta (который будет в июне), чтобы посмотреть, совместима ли ваша кодовая база со всеми изменениями, депрессиями в новой версии, включая устаревание и удаление модулей и функций.

Узнайте, как прокачаться в других специальностях или освоить их с нуля:

Другие профессии и курсы

ссылка на оригинал статьи https://habr.com/ru/company/skillfactory/blog/550540/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *