Перевод подготовлен совместно с факультетом Python-разработки GeekBrains. Автор оригинального текста Farhad Malik.
Как и большинство поклонников Python, я очень рад изучать и использовать новейшие функции. В этой статье будет представлен обзор возможностей Python 3.9, которые необходимо знать.
Я ознакомился с примечаниями к выпуску, посмотрел обсуждения и составил исчерпывающий гайд о том, что из себя представляют эти функции и как они работают.
Было добавлено несколько новых функций, включая объединение и обновление словарей, строковые методы и внедрение модуля zoneinfo. Также нам представили стабильный и высокопроизводительный парсер.
Давайте разбираться с нововведениями.
1. Операторы обновления и слияния словарей
Во встроенный класс dict
добавлено два оператора: |
и |=
.
| используется для объединения словарей, |= – для их обновления.
PEP 584
Код:
Объединение: |
Обновление: |=
В случае конфликта ключей верным будет считаться крайнее правое значение. Это соответствует поведению аналогичных операций dict
.
Более детально
Как мы видим, добавлены новые операторы |
и |=
.
|
можно рассматривать как оператор +
(сложения) в списках, а |=
– как оператор +=
(расширения).
В Python 3.8 есть несколько способов слияния и обновления словарей.
К примеру, можно использовать first_dict.update(second_dict)
. Проблема этого метода в том, что он изменит first_dict
на месте. Чтобы этого избежать, нужно объявить временную переменную, сохранить в ней first_dict
, а затем выполнить операцию обновления. Но появляется лишняя строка кода, просто чтобы оператор объединения/обновления работал.
Также мы можем применить {**first_dict, **second_dict}
. Сложность этого метода в том, что его трудно обнаружить и сложнее понять смысл кода. Кроме того, исключаются типы mapping и учитывается только тип dict. Например, если first_dict
– это defaultdict
, а second_dict
– это тип dict
, то программа завершится ошибкой.
Этот способ не работает с подклассами dict
, которые содержат функцию _init_
.
Наконец, библиотека collections содержит функцию ChainMap
. Она может принять два словаря, как ChainMap(first_dict, second_dict)
, и вернуть объединенный словарь, но об этой библиотеке знают немногие.
Для получения более подробной информации вы можете ознакомиться с источником.
https://www.python.org/dev/peps/pep-0584
2. Новый высокопроизводительный парсер на основе PEG
С Python 3.9 можно отказаться от использования LL (1) в пользу более гибкого и стабильного синтаксического анализатора на основе PEG.
PEP: 617
Более детально
Текущий парсер CPython основан на LL (1). Грамматика основана на LL (1), что позволяет парсить ее с помощью LL (1) анализатора. Парсер LL (1) работает сверху вниз и анализирует входные данные слева направо. Грамматика является контекстно-свободной, поэтому контекст токенов не учитывается.
Python 3.9 предлагает заменить его новым парсером на основе PEG, который снимет ограничения Python грамматики LL (1). Будет удален ряд хаков, существующих в текущем синтаксическом анализаторе. В долгосрочной перспективе это снизит стоимость обслуживания.
Несмотря на то, что синтаксические анализаторы и грамматики LL (1) просты в реализации, ограничения не позволяют им выражать общие конструкции естественным образом для разработчика языка и читателя. Парсер смотрит только на один токен вперед, чтобы различать возможности.
Оператор выбора | упорядоченный. Рассмотрим следующее правило:
rule: A|B|C
Контекстно-свободный парсер грамматики LL (1) будет генерировать конструкции, которые при заданной входной строке определят, нужно ли расширять A, B или C. Анализатор PEG отличается. Он проверит, успешна ли первая переменная, и только в случае провала перейдет ко второй или третьей.
Парсер PEG генерирует ровно одно допустимое дерево для строки. Он определенный, в отличие парсер LL (1).
Синтаксический анализатор PEG также напрямую генерирует узлы AST для правила через грамматические действия. За счет этого удается избежать создания промежуточных шагов.
Парсер PEG был тщательно протестирован. У него отлажена производительность. Поэтому для большинства инструкций он расходует примерно 10% от объема памяти и вычислительных ресурсов текущего анализатора. Все благодаря тому, что не создается промежуточное синтаксическое дерево.
Я опустил некоторые детали, чтобы не усложнять прочтение статьи. Для получения более подробной информации вы можете ознакомиться с источником.
https://www.python.org/dev/peps/pep-0617
3. Новые строковые функции для удаления префикса и суффикса
К объекту str
добавлено две новых функции.
1. Первая удаляет префикс – str.removeprefix(‘префикс’)
.
2. Вторая удаляет суффикс – str.removesuffix(‘суффикс’)
.
PEP: 616
Код:
Более детально
Одна из обыденных задач в приложении data science, которое включает в себя манипулирование текстом – удалить префикс/суффикс строк. Добавленные к объекту str
функции можно использовать для удаления ненужных префиксов и суффиксов из строки.
Как мы уже знаем, первая функция удаляет префикс. Это str.removeprefix(‘префикс’)
. Вторая функция удаляет суффикс. Это str.removesuffix(‘суффикс’)
.
Строка – это набор символов, и каждый символ имеет индекс в строке. Индексы можно использовать вместе с :
, чтобы вернуть подмножество строки. Эта функция известна как slice
(срез) строки.
Если мы говорим о функциях, они проверяют, начинается ли строка с префикса (заканчивается ли она суффиксом), и если да, то возвращают строку без префикса или после суффикса, используя функцию среза str[:]
.
Поскольку эти функции являются частью стандартной библиотеки, мы получаем согласованный, менее хрупкий, высокопроизводительный и более наглядный API.
Для получения более подробной информации вы можете ознакомиться с источником.
https://www.python.org/dev/peps/pep-0616
4. Подсказки типов для встроенных универсальных типов
Аннотирование программ стало проще за счет удаления иерархии параллельных типов в Python.
Добавлена поддержка универсального синтаксиса во всех стандартных коллекциях, доступных в модуле набора текста.
Мы можем использовать встроенные типы коллекций list
или dict
в качестве универсальных типов вместо использования typing.List
или typing.Dict
в сигнатуре нашей функции.
Код стал выглядеть чище, а его понимание и объяснение упростилось.
PEP: 585
Более детально
Несмотря на то, что Python – это язык с динамической типизацией, аннотация типов в программе позволяет проводить самоанализ. Впоследствии аннотацию можно использовать для создания API проверки типов во время выполнения.
Как я говорил ранее, в Python 3.9 добавили поддержку универсального синтаксиса во всех стандартных коллекциях, доступных в модуле набора текста.
Универсальный тип – это обычно контейнер, к примеру list
. Это тип, который можно параметризовать. Параметризованный тип – это пример универсального дженерика с ожидаемыми типами для элементов контейнера типа list[str]
.
Мы можем использовать встроенные типы коллекций list
или dict
в качестве универсальных типов вместо использования typing.List
или typing.Dict
.
Например, мы могли бы управлять проверкой типов среды выполнения Python, аннотируя код:
Ряд функций статической типизации был постепенно построен поверх существующей среды выполнения Python. Некоторые из них были ограничены существующим синтаксисом и поведением во время выполнения. Поэтому в модуле типизации возникла дублированная иерархия коллекций из-за дженериков.
Например, мы увидим typing.List
, typing.Dictionary
вместе со встроенными list
, dictionary
и т. д. Это позволяет писать код:
Для получения более подробной информации вы можете ознакомиться с источником.
https://www.python.org/dev/peps/pep-0585
5. Поддержка часового пояса IANA в DateTime
Модуль zoneinfo
был создан в качестве поддержки базы данных часовых поясов IANA. Эта поддержка была добавлена в стандартную библиотеку.
PEP: 615
Часовые пояса IANA часто называют tz
или zone info. Существует много часовых поясов IANA с разными путями поиска для указания часового пояса IANA объекта datetime
. Например, мы можем передать имя пути поиска объекту datetime
как Continent/City, чтобы установить его tzinfo
.
dt = datetime(2000, 01, 25, 01, tzinfo=ZoneInfo("Europe/London"))
Если мы передадим неверный ключ, возникнет ошибка zoneinfo.ZoneInfoNotFoundError
.
Более детально
Библиотека datetime
используется для создания объекта datetime
и указания его часового пояса, путем установки свойства tzinfo
. Все может обернуться созданием сложных правил часового пояса при использовании базового показателя datetime.tzinfo
.
В большинстве случаев нужно просто установить объект и его часовой пояс: UTC, локальный часовой пояс системы, или часовой пояс IANA.
Можно создать объект zoneinfo.ZoneInfo(key)
, где ключ имеет строковый тип, указывающий путь поиска файла зоны в базе данных часовых поясов системы. Объект zoneinfo.ZoneInfo(key)
может быть создан и установлен как свойство tzinfo
объекта datetime
.
Код:
Для получения более подробной информации вы можете ознакомиться с источником.
https://www.python.org/dev/peps/pep-0615
6. Возможность отмены одновременных фьючерсов.
В concurrent.futures.Executor.shutdown()
добавлен новый параметр cancel_futures
.
Он отменяет все отложенные фьючерсы, которые не были запущены.
До версии 3.9 процесс ожидал их завершения перед завершением работы исполнителя.
Пояснение
Новый параметр cancel_futures
был добавлен в ThreadPoolExecutor
и ProcessPoolExecutor
. Это работает так: если его значение – True
, все ожидающие фьючерсы отменяются при вызове функции shutdown()
.
При выполнении shutdown()
интерпретатор проверяет, не собран ли исполнитель сборщиком мусора. Если он все еще находится в памяти, он получает все ожидающие обработки элементы, а затем отменяет фьючерсы.
Когда не остается незавершенных рабочих элементов, он завершает работу.
Для получения более подробной информации вы можете ознакомиться с источником.
https://bugs.python.org/issue30966
7. Улучшения AsyncIO и многопроцессорности
В библиотеку asyncio
и многопроцессорную обработку были внесен ряд улучшений.
Например:
1. Параметр reuse_address
asyncio.loop.create_datagram_endpoint()
больше не поддерживается из-за серьезных пробелов в безопасности.
2. Добавлены новые сопрограммы: shutdown_default_executor()
и asyncio.to_thread(). shutdown_default_executor
назначает завершение работы для исполнителя по умолчанию, который ждет завершения ThreadPoolExecutor. asyncio.to_thread()
в основном используется для запуска функций, связанных с вводом-выводом, в отдельном потоке, чтобы избежать блокировки цикла событий.
Что касается улучшений библиотеки многопроцессорности, в класс multiprocessing.SimpleQueue
был добавлен новый метод close()
.
Этот метод точно закрывает очередь. Это гарантирует, что очередь будет закрыта и не останется дольше ожидаемого. Помните, что методы get()
, put()
, empty()
не должны вызываться после закрытия очереди.
Для получения более подробной информации вы можете ознакомиться с источником.
https://bugs.python.org/issue30966
8. Постоянные ошибки импорта пакетов
Основная проблема импортирования библиотек Python заключалась в несогласованном поведении, когда относительный импорт проходил мимо пакета верхнего уровня.
Встроенная функция __import__()
вызывает ошибку ValueError
, а importlib.__import__()
вызывает ошибку ImportError
.
Теперь это исправили. __import__()
вызывает ImportError
вместо ValueError
.
Для получения более подробной информации вы можете ознакомиться с источником.
https://bugs.python.org/issue37444
9. Генерация случайных байтов
Еще одна функция, добавленная в версии 3.9 – random.Random.randbytes()
. Эта функция может использоваться для генерации случайных байтов.
Можно генерировать случайные числа, но что, если нужно генерировать случайные байты? Раньше разработчикам приходилось для этого проявлять изобретательность. Хотя можно использовать os.getrandom()
, os.urandom()
или secrets.token_bytes()
, но нельзя генерировать псевдослучайные паттерны.
К примеру, чтобы гарантировать, что случайные числа генерируются с ожидаемым поведением и процесс воспроизводится, обычно используется модуль random.Random
.
В результате был введен метод random.Random.randbytes()
. Он также может генерировать случайные байты контролируемым способом.
Для получения более подробной информации вы можете ознакомиться с источником.
https://bugs.python.org/issue40286
10. Исправление функции замены строки
Раньше "".replace("", s, n)
возвращал пустую строку вместо s
для всех ненулевых n
.
Этот баг сбивал с толку пользователей и приводил к нестабильному поведению приложений.
В Python 3.9 проблема была устранена, и теперь функция замены совместима с "".replace("", s)
.
Работает она следующим образом: для заданного максимального аргумента вхождения замены, набор символов из строки заменяется новым набором символов:
string.replace(s, old, new[, maxreplace])
В этом случае возвращается копия строки s со всеми вхождениями старой подстроки, замененной на новую. Если указан необязательный аргумент maxreplace, заменяются первые экземпляры maxreplace.
До этого функция replace имела непоследовательное поведение
Теперь:
В Python 3.9 также был исключен ряд избыточных функций, таких как Py_UNICODE_MATCH
.
Если вы хотите узнать больше об этих улучшениях, прочтите официальное руководство здесь.
Как научиться программировать на Python максимально быстро и качественно?
В условиях повышенной конкуренции среди джунов, пойти учиться на курсы с преподавателями — самый прагматичный вариант, который позволит быстро и качественно освоить базовые навыки программирования и положить 5 проектов в портфолио. Преподаватель прокомментирует домашние задания, поделится полезными советами, когда надо подбодрит или даст «волшебного» пинка.
На курсе «Основы программирования на Python» с преподавателем вы научитесь:
- работать в двух интегрированных средах разработки — PyCharm и Jupyter Notebook;
- парсить веб-страницы;
- создавать ботов для Telegram и Instagram;
- работать с данными для различных материалов и дальнейшего анализа;
- тестировать код.
Плюс положите 5 проектов в портфолио.
Комментарии