Сегодня, 05.10.2020 ожидается выход стабильной версии Python 3.9.0. Новая версия будет получать обновления с исправлениями примерно каждые 2 месяца в течение примерно 18 месяцев. Через некоторое время после выпуска финальной версии 3.10.0 будет выпущено девятое и последнее обновление с исправлением ошибок 3.9.
«This is the first version of Python to default to the 64-bit installer on Windows. The installer now also actively disallows installation on Windows 7.»
«Это первая версия Python, по умолчанию использующая 64-разрядный установщик в Windows. Программа установки теперь также запрещает установку в Windows 7.»
Я прочитал примечания к выпуску Python 3.9 и связанные с ними обсуждения. Основываясь на информации, я хотел написать исчерпывающее руководство, чтобы каждый мог получить представление о функциях вместе с их подробной работой.
UPD:
Переход на стабильные годовые циклы выпуска, см. PEP 602
alec_kalinin
«Каждая новая версия Python теперь будет выходить в октябре. Python 3.10 выйдет в октябре 2021, Python 3.11 в октябре 2022. А каждые два месяца будет выходить бакфикс. Для меня это великолепная новость, теперь можно четко планировать upgrade python окружения.»
PEP 584
Этот PEP предлагает добавить операторы слияния ( | ) и обновления ( | = ) во встроенный класс dict.
Для слияния: |
>>> a = {'milk': 'prostokvashino', 'сheese': 'cheddar'}
>>> b = {'milk': 1, 'сheese': 2, 'bread': 3}
>> > а | b
{'milk': 1, 'сheese': 2, 'bread': 3}
>>> b | a
{'milk': 'prostokvashino', 'сheese': 'cheddar', 'bread': 3}
Для обновления: |=
>>> a | = b
>>> a
{'milk': 1, 'сheese': 2, 'bread': 3}
Ключевое правило, которое следует запомнить, заключается в том, что если есть какие-либо конфликты ключей, то самое правое значение будет сохранено.
Безусловно, у многих питонистов возникнет вопрос, зачем это нужно, если уже есть привычный всем вариант
{**d1, **d2}
На этот вопрос был дан ответ в самом PEP:
Распаковка словарей выглядит некрасиво, и ее нелегко обнаружить. Мало кто сможет догадаться, что это означает, когда они впервые увидят это.
Прошу прощения за PEP 448, но даже если вы знаете о ** d в более простом контексте, если бы вы спросили типичного пользователя Python, как объединить два dict в новый, я сомневаюсь, что многие люди подумают о {**d1, **d2}. Я знаю, что сам забыл об этом, когда началась эта ветка!
PEP 585
Generics подсказки типов в стандартных коллекциях.
Generic — тип, который можно параметризовать, некий контейнер. Также известен как параметрический тип или универсальный тип.
В выпуске включена поддержка универсального синтаксиса во всех стандартных коллекциях, доступных в настоящее время в модуле «Typing». Мы можем использовать типы list или dict в качестве универсальных типов вместо использования typing.List или typing.Dict.
Было:
from typing import List
a: List[str] = list()
def read_files(files: List[str]) -> None:
pass
Стало:
a: list[str] = list()
def read_files(files: list[str]) -> None:
pass
list
dict
set
frozenset
type
collections.deque
collections.defaultdict
collections.OrderedDict
collections.Counter
collections.ChainMap
collections.abc.Awaitable
collections.abc.Coroutine
collections.abc.AsyncIterable
collections.abc.AsyncIterator
collections.abc.AsyncGenerator
collections.abc.Iterable
collections.abc.Iterator
collections.abc.Generator
collections.abc.Reversible
collections.abc.Container
collections.abc.Collection
collections.abc.Callable
collections.abc.Set # typing.AbstractSet
collections.abc.MutableSet
collections.abc.Mapping
collections.abc.MutableMapping
collections.abc.Sequence
collections.abc.MutableSequence
collections.abc.ByteString
collections.abc.MappingView
collections.abc.KeysView
collections.abc.ItemsView
collections.abc.ValuesView
contextlib.AbstractContextManager # вместо typing.ContextManager
contextlib.AbstractAsyncContextManager # вместо typing.AsyncContextManager
re.Pattern # вместо typing.Pattern, typing.re.Pattern
re.Match # вместо typing.Match, typing.re.Match
PEP 615
Поддержка базы данных часовых поясов IANA в стандартной библиотеке.
IANA часовые пояса часто называют tz или zone info. Существует большое количество часовых поясов IANA с разными путями поиска для указания часового пояса IANA для объекта даты и времени. Например, мы можем передать имя пути поиска как Континент/Город datetime объекту, чтобы установить его tzinfo.
dt: datetime = datetime(2000, 01, 25, 01, tzinfo=ZoneInfo("Europe/London"))
Если мы передадим неверный ключ, будет вызвано исключение
zoneinfo.ZoneInfoNotFoundError
PEP 616
Новые строковые функции для удаления префикса и суффикса.
К str объекту добавлены две новые функции.
- Первая функция удаляет префикс.
str.removeprefix(prefix)
- Вторая функция удаляет суффикс.
str.removesuffix(suffix)
>>> 'hello_world'.removeprefix ('hello_')
world
>>> 'hello_world'.removesuffix ('_world')
hello
PEP 617
Версия Python 3.9 предлагает заменить текущий синтаксический анализатор Python на основе LL (1) новым синтаксическим анализатором на основе PEG, который является высокопроизводительным и стабильным.
Текущий парсер CPython основан на LL (1). Впоследствии грамматика основана на LL (1), что позволяет анализировать ее с помощью анализатора LL (1). Парсер LL (1) работает сверху вниз. Кроме того, он анализирует входные данные слева направо. Текущая грамматика является контекстно-свободной грамматикой, поэтому контекст токенов не принимается во внимание.
Версия Python 3.9 предлагает заменить его новым парсером на основе PEG, что означает, что он снимет текущие ограничения Python грамматики LL (1). Кроме того, в текущий синтаксический анализатор внесены исправления, в которые добавлен ряд хаков, которые будут удалены. В результате это снизит стоимость обслуживания в долгосрочной перспективе.
Например, хотя синтаксические анализаторы и грамматики LL (1) просты в реализации, ограничения не позволяют им выражать общие конструкции естественным образом для разработчика языка и читателя. Парсер смотрит только на один токен вперед, чтобы различать возможности.
issue30966
Возможность отмены одновременных фьючерсов.
Новый параметр cancel_futures был добавлен в concurrent.futures.Executor.shutdown().
В этом параметре все отложенные фьючерсы, которые еще не начались. До версии 3.9 процесс ожидал их завершения перед завершением работы исполнителя.
Новый параметр cancel_futures был добавлен в ThreadPoolExecutor и ProcessPoolExecutor. Он работает, когда значение параметра равно True, тогда все ожидающие фьючерсы будут отменены при вызове функции shutdown().
Когда shutdown() выполняется, интерпретатор проверяет, не собран ли исполнитель сборщиком мусора. Если он все еще находится в памяти, он получает все ожидающие обработки элементы, а затем отменяет фьючерсы.
issue30966
В этом выпуске в библиотеку asyncio и multiprocessing был внесен ряд улучшений.
Например,
- reuse_address параметр asyncio.loop.create_datagram_endpoint() больше не поддерживаются из-за значительных проблем в области безопасности.
- Добавлены новые coroutines, shutdown_default_executor() и сопрограммы asyncio.to_thread(). Новый вызов asyncio.to_thread() используется для запуска функций, связанных с вводом-выводом, в отдельном потоке, чтобы избежать блокировки цикла событий.
Что касается улучшений библиотеки multiprocessing, в класс multiprocessing.SimpleQueue был добавлен новый метод close().
Этот метод явно закрывает очередь. Это гарантирует, что очередь будет закрыта и не останется дольше ожидаемого. Важно помнить, что методы get(), put(), empty() нельзя вызывать после закрытия очереди.
issue37444
Исправление ошибки импорта пакетов.
Основная проблема с импортом библиотек Python до выпуска 3.9 заключалась в несогласованном поведении импорта в Python, когда относительный импорт проходил мимо его пакета верхнего уровня.
builtins.__import__() вызывал ValueError то время как importlib.__import__() вызывал ImportError.
Сейчас это исправлено, . __Import __ () теперь вызывает ImportError вместо ValueError.
issue40286
Еще одна функция, которая была добавлена в версии 3.9, — это random.Random.randbytes(). Она может использоваться для генерации случайных байтов.
Мы можем генерировать случайные числа, но что, если нам нужно было генерировать случайные байты? До версии 3.9 разработчикам приходилось проявлять изобретательность, чтобы генерировать случайные байты. Хотя мы можем использовать, os.getrandom(), os.urandom() или secrets.token_bytes() но не можем генерировать псевдослучайные шаблоны.
Например, чтобы гарантировать, что случайные числа генерируются с ожидаемым поведением и процесс воспроизводится, мы обычно используем seed с модулем random.Random.
В результате был введен метод random.Random.randbytes(). Он генерирует случайные байты.
issue28029
Исправление функции замены строки.
Принцип работы функции replace заключается в том, что для заданного максимального аргумента замены вхождения она заменяет набор символов из строки новым набором символов.
Чтобы дополнительно объяснить проблему, до версии 3.9 функция replace имела несогласованное поведение:
Можно было бы ожидать увидеть blog
"" .replace ("", "blog", 1)
>>> ''
Можно было бы ожидать увидеть |
"" .replace ("", "|", 1)
>>> ''
"" .replace ("", "prefix")
>>> 'prefix'
issue39648, issue39479, issue39288, issue39310
Изменения в модуле «math».
Возвращает наименьшее общее кратное целых аргументов:
>>> import math
>>> math.lcm(48,72,108)
432
Возвращает наибольший общий делитель целочисленных аргументов. В более ранней версии поддерживалось только два аргумента. Добавлена поддержка произвольного количества аргументов:
>>> import math
>>> math.gcd(9,12,21)
3
Вычисляет ближайшее к "x" число с плавающей точкой, если двигаться в направлении "y".
>>> math.nextafter(2, -1)
1.9999999999999998
Этот метод возвращает значение младшего бита числа с плавающей запятой x.
>>> 1 - math.ulp(1)
0.9999999999999998
>>> math.nextafter(1, -1) + math.ulp(1)
1.0
issue38870
В модуль ast добавили метод unparse.
Новый метод может использоваться для создания строки с кодом и его последующего выполнения.
>>> import ast
>>> parsed = ast.parse('from sys import platform; print(platform)')
>>> unparsed_str = ast.unparse(parsed)
>>> print(unparsed_str)
from sys import platform
print(platform)
>>> exec(unparsed_str)
win32
issue39507, issue39509
Добавление новых кодов в http.HTTPStatus.
418 IM_A_TEAPOT
103 EARLY_HINTS
425 TOO_EARLY
UPD:
PEP 614
Ослабление грамматических ограничений для декораторов.
Было:
buttons = [QPushButton(f'Button {i}') for i in range(10)]
button_0 = buttons[0]
@button_0.clicked.connect
def spam():
...
button_1 = buttons[1]
@button_1.clicked.connect
def eggs():
...
То теперь можно убрать лишние присваивания и вызывать напрямую:
buttons = [QPushButton(f'Button {i}') for i in range(10)]
@buttons[0].clicked.connect
def spam():
...
@buttons[1].clicked.connect
def eggs():
...
«Кортеж должен быть заключен в круглые скобки»
Это основано на видении Гвидо в том же письме. Цитата:
Но запятых я не допущу. Я не могу согласиться с этим
@f, g def pooh(): ...
Это может привести неопытных программистов к выводу, что таким способом можно вызывать несколько декораторов подряд. Скобки необходимы, чтобы все прояснить без дополнительных ограничений или сложного синтаксиса.
issue17005
Новый модуль graphlib предоставляет функциональные возможности для топологической сортировки графа хешируемых узлов.
Подробнее можно ознакомиться в документации.
>>> from graphlib import TopologicalSorter
>>> graph = {'E': {'C', 'F'}, 'D': {'B', 'C'}, 'B': {'A'}, 'A': {'F'}}
>>> ts = TopologicalSorter(graph)
>>> tuple(ts.static_order())
('C', 'F', 'E', 'A', 'B', 'D')
>>> tuple(ts.static_order())
('F', 'C', 'A', 'E', 'B', 'D')
Граф можно передавать не сразу, а заполнять TopologicalSorter с помощью метода add. Кроме того класс адаптирован к параллельным вычислениям и может быть использован, например, для создания очереди задач.
issue37630, issue40479
Обновление библиотеки hashlib.
Теперь hashlib может использовать хэши SHA3 и SHAKE XOF из OpenSSL.
Встроенные хэш-модули теперь могут быть отключены или выборочно включены, например, для принудительного использования реализации на основе OpenSSL.
Оптимизация
Сводка улучшений производительности от Python 3.4 до Python 3.9:
Python version 3.4 3.5 3.6 3.7 3.8 3.9
-------------- --- --- --- --- --- ---
Variable and attribute read access:
read_local 7.1 7.1 5.4 5.1 3.9 4.0
read_nonlocal 7.1 8.1 5.8 5.4 4.4 4.8
read_global 15.5 19.0 14.3 13.6 7.6 7.7
read_builtin 21.1 21.6 18.5 19.0 7.5 7.7
read_classvar_from_class 25.6 26.5 20.7 19.5 18.4 18.6
read_classvar_from_instance 22.8 23.5 18.8 17.1 16.4 20.1
read_instancevar 32.4 33.1 28.0 26.3 25.4 27.7
read_instancevar_slots 27.8 31.3 20.8 20.8 20.2 24.5
read_namedtuple 73.8 57.5 45.0 46.8 18.4 23.2
read_boundmethod 37.6 37.9 29.6 26.9 27.7 45.9
Variable and attribute write access:
write_local 8.7 9.3 5.5 5.3 4.3 4.2
write_nonlocal 10.5 11.1 5.6 5.5 4.7 4.9
write_global 19.7 21.2 18.0 18.0 15.8 17.2
write_classvar 92.9 96.0 104.6 102.1 39.2 43.2
write_instancevar 44.6 45.8 40.0 38.9 35.5 40.7
write_instancevar_slots 35.6 36.1 27.3 26.6 25.7 27.7
Data structure read access:
read_list 24.2 24.5 20.8 20.8 19.0 21.1
read_deque 24.7 25.5 20.2 20.6 19.8 21.6
read_dict 24.3 25.7 22.3 23.0 21.0 22.5
read_strdict 22.6 24.3 19.5 21.2 18.9 21.6
Data structure write access:
write_list 27.1 28.5 22.5 21.6 20.0 21.6
write_deque 28.7 30.1 22.7 21.8 23.5 23.2
write_dict 31.4 33.3 29.3 29.2 24.7 27.8
write_strdict 28.4 29.9 27.5 25.2 23.1 29.8
Stack (or queue) operations:
list_append_pop 93.4 112.7 75.4 74.2 50.8 53.9
deque_append_pop 43.5 57.0 49.4 49.2 42.5 45.5
deque_append_popleft 43.7 57.3 49.7 49.7 42.8 45.5
Timing loop:
loop_overhead 0.5 0.6 0.4 0.3 0.3 0.3
Сценарий теста отображает время в наносекундах. Тесты были выполнены на процессоре Intel Core i7-4960HQ. Код теста можно найти в репозитории по адресу "Tools/scripts/var_access_benchmark.py".
Благодарю за внимание.
Ссылка на официальное руководство Python 3.9.
Отпишитесь в комментарии, если что-то было упущено.
Автор: Леонид