nikita.kopot 21 ноября 2020

🐍 10 классных функций Python 3.9

Новая версия популярного языка программирования вышла не так давно. Рассмотрим наиболее интересные функции Python 3.9, изучить которые стоит как новичкам, так и профессионалам.
🐍 10 классных функций Python 3.9

Перевод подготовлен совместно с факультетом Python-разработки GeekBrains. Автор оригинального текста Farhad Malik.

***

Как и большинство поклонников Python, я очень рад изучать и использовать новейшие функции. В этой статье будет представлен обзор возможностей Python 3.9, которые необходимо знать.

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

***

Было добавлено несколько новых функций, включая объединение и обновление словарей, строковые методы и внедрение модуля zoneinfo. Также нам представили стабильный и высокопроизводительный парсер.

Давайте разбираться с нововведениями.

1. Операторы обновления и слияния словарей

Во встроенный класс dict добавлено два оператора: | и |=.

| используется для объединения словарей, |= – для их обновления.

PEP 584

Код:

Объединение: |

        >>> a = {‘farhad’: 1, 'blog’: 2, 'python’: 3}

>>> b = {’farhad’: 'malik’, 'topic’: 'python3.9’}

>>> a | b

{’blog’: 2, 'python’: 3, ’farhad’:’malik’, 'topic’: 'python3.9’}

>>> b | a

{’farhad’: 1,’blog’: 2, 'python’: 3, 'topic’:’python3.9’ }
    

Обновление: |=

        >>> a |= b

>>> a

{’blog’: 2, 'python’: 3,’farhad’:’malik’}
    

В случае конфликта ключей верным будет считаться крайнее правое значение. Это соответствует поведению аналогичных операций 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

Код:

        'farhad_python'.removeprefix('farhad_')

#возвращает python

'farhad_python'.removesuffix('_python')

#возвращает farhad
    

Более детально

Одна из обыденных задач в приложении 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, аннотируя код:

        print_value(input: str):

print(input)

# Мы получим уведомление, если входные данные не являются строкой
    

Ряд функций статической типизации был постепенно построен поверх существующей среды выполнения Python. Некоторые из них были ограничены существующим синтаксисом и поведением во время выполнения. Поэтому в модуле типизации возникла дублированная иерархия коллекций из-за дженериков.

Например, мы увидим typing.List, typing.Dictionary вместе со встроенными list, dictionary и т. д. Это позволяет писать код:

        def read_blog_tags(tags: list[str]) -> None:

for tag in tags:

print("Tag Name", tag)
    
***

Для получения более подробной информации вы можете ознакомиться с источником.

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.

Код:

        from zoneinfo import ZoneInfo

from datetime import datetime

dt = datetime(2000, 01, 25, 01, tzinfo=ZoneInfo("America/Los_Angeles"))
    
***

Для получения более подробной информации вы можете ознакомиться с источником.

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 имела непоследовательное поведение

        "".replace("", "blog", 1)

Возвращает ''

Ожидалось увидеть blog

"".replace("", "|", 1)

Возвращает ''

Ожидалось увидеть |

"".replace("", "prefix")

Но возвращает 'prefix'
    

Теперь:

        "".replace("",s,n) возвращает s вместо пустой строки для всех ненулевых n
    

В Python 3.9 также был исключен ряд избыточных функций, таких как Py_UNICODE_MATCH.

Если вы хотите узнать больше об этих улучшениях, прочтите официальное руководство здесь.

***

Как научиться программировать на Python максимально быстро и качественно?

В условиях повышенной конкуренции среди джунов, пойти учиться на курсы с преподавателями — самый прагматичный вариант, который позволит быстро и качественно освоить базовые навыки программирования и положить 5 проектов в портфолио. Преподаватель прокомментирует домашние задания, поделится полезными советами, когда надо подбодрит или даст «волшебного» пинка.

На курсе «Основы программирования на Python» с преподавателем вы научитесь:

  • работать в двух интегрированных средах разработки — PyCharm и Jupyter Notebook;
  • парсить веб-страницы;
  • создавать ботов для Telegram и Instagram;
  • работать с данными для различных материалов и дальнейшего анализа;
  • тестировать код.

Плюс положите 5 проектов в портфолио.

Источники

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик С#
от 200000 RUB до 400000 RUB
Java Team Lead
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ