Краще пізно, ніж ніколи: нові фічі Python 3.9
Мене звати Володимир Данилевський, я працюю на позиції Python Developer в компанії Django Stars. Моя стаття пояснює нові фічі Python 3.9, реліз якого був 5 жовтня 2020 року.
Останній реліз Python 3.9 містить фічі для зворотної сумісності, як, наприклад, collections.abc.Mapping, що буде видалений у Python 3.10. Використовуйте тестовий режим щоб побачити всі deprecated errors у вашому коді.
IANA база часових поясів PEP 615
Наразі додалась ще одна «batteries» — zoneinfo.ZoneInfo
Спойлер: для Windows користувачам потрібно завантажити tzdata: python -m pip install tzdata
Цей модуль допоможе для tz-aware timestamps. Так, наприклад:
From datetime import datetime From zoneinfo import ZoneInfo timestamp = datatime.now(tz=ZoneInfo("Europe/Kiev"))
Він буде мати читабельну мітку часового поясу, також таким же чином можливо змінити таймзону: timestamp = timestamp.astimezone(ZoneInfo("Europe/London"))
Виконавши команду zoneinfo.available_timezones() можна отримати всі можливі лейби для ZoneInfo, які можна використати для timestamps.
Також цей функціонал можна імпортувати у минулі версії Python через модуль backports який є до версії 3.6 backward compatibility.
try: Import zoneinfo except ImportError: from backports import zoneinfo
Новий синтаксис оновлення словників PEP 584
Одна з основних дата-структур отримала новий оператор злиття.
Раніше були наступні види злиття словарів:
>>> fifa_hosts = {2006: "Germany", 2010: "South Africa", 2014: "Brazil"} >>> winter_olympics = {2006: "Italy", 2010: "Canada", 2018: "South Korea"} >>> {**fifa_hosts, **winter_olympics} {2006: 'Italy', 2010: 'Canada', 2014: 'Brazil', 2018: 'South Korea'} >>> merged = fifa_hosts.copy() >>> for key, value in winter_olympics.items(): ... merged[key] = value ... >>> merged {2006: 'Italy', 2010: 'Canada', 2014: 'Brazil', 2018: 'South Korea'}
Тепер з’явились два нових методи Python для словарів: злиття (|) та in-place злиття (|=).
>>> winter_olympics = {2006: "Italy", 2010: "Canada", 2018: "South Korea"} >>> fifa_hosts | winter_olympics {2006: 'Italy', 2010: 'Canada', 2014: 'Brazil', 2018: 'South Korea'} >>> fifa_hosts |= winter_olympics >>> fifa_hosts {2006: 'Italy', 2010: 'Canada', 2014: 'Brazil', 2018: 'South Korea'}
Більш гнучке використання декораторів PEP 614
Зазвичай декоратор має бути іменованим callable objecte — функція або class з __call__
методом. PEP 614 дозволяє бути любим callable. Приклад:
import functools def normal(func): return func def shout(func): @functools.wraps(func) def shout_decorator(*args, **kwargs): return func(*args, **kwargs).upper() return shout_decorator def whisper(func): @functools.wraps(func) def whisper_decorator(*args, **kwargs): return func(*args, **kwargs).lower() return whisper_decorator
DECORATORS = {"normal": normal, "shout": shout, "whisper": whisper} voice = input(f"Choose your voice ({', '.join(DECORATORS)}): ") @DECORATORS[voice] def get_story(): return """ Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, "and what is the use of a book," thought Alice "without pictures or conversations?" """ print(get_story())
Покращення
Нові методи для str
— removeprefix, removesuffix PEP 616
На перший погляд, здається, що це ще один велосипед до вже існуючих: .strip .lstrip .rstrip
Але можна побачити, що вони використовують переданий параметр як набір символів для видалення, а не півстроку. Тому наприклад:
'123 321'.strip('12') '3 3'
Це вже давно є причиною створення подібних баг-репортів
Нові методи .removesuffix</cpde> <code>.removeprefix
видаляють одне входження підстроки і повертає нову строку з видаленою підстрокою.
Більш гнучкі type annotations та type hints PEP 593
Анотації для функцій та змінних з’явилися ще в Python 3.0. Це додало можливість анотувати довільною інформацією про змінні та зворотні значення. Зараз подібне використання майже повністю витіснено type hints — за допомогою mypy можливо провести статичний аналіз коду.
Тепер на додачу до звичайних type hints можна додати коментар що буде доступний в __annotations__ та в хелпері get_type_hints
З модуля typing
from typing import Annotated Leg = Annotated[float, "Side leg lenght"] Hypotenuse = Annotated[float, "Hypotenuse lenght"] def calculate_leg_lenght(existing_leg: Leg, hypotenuse: Hypotenuse) -> Leg: return (hypotenuse**2 - existing_leg**2) ** 0.5
Новий більш потужний парсер PEP 617
Найбільш масштабною змінною став реліз нового парсер. Хоча цього не видно, але це важливий крок на шляху нових рішень та розширення існуючого функціоналу. З самого початку Python використовув простий LL(1) parser для парсингу коду в дерево. Парсить це він в один прохід без повернень, з чого витікають обмеження та проблеми. Так наприклад, це дозволить реалізувати tructural pattern matching (PEP 634).
Guido van Rossum зробив детальний розбір переваг та деталей щодо нового парсеру — PEG (parsing expression grammar) parsers.
Зараз головною ціллю core devs є приводження AST нового парсера до вигляду результату зі старого. Якщо у вас виникають проблеми, або ви хочете порівняти результати нового і старого парсеру, можна використати -X oldparser флаг інтерпретатору.
Покращення швидкодії та багато іншого
Покращено присвоювання змінної в comprehension expression. Присвоювання у конструкції for variable in [some_iterable]
тепер таке ж швидке як і звичайне variable = some_value
.
Покращено операцію floor_division(//)
— тепер вона ~ приблизно на 35% швидше.
Python 3.6.6 timeit.timeit('a = 3.5; b = a // 2', number=500000) 0.05193754100002934 Python 3.9.3 timeit.timeit('a = 3.5; b = a // 2', number=500000) 0.02754197199999453
Під капотом покращили виклики для створення таких built-ins як — range, tuple, set, frozenset, list, dict завдяки використанню нового «vectorcall protocol» PEP 590. На практиці я не можу це підтвердити як і в треді розробників
UPD. Дякую Дмитру, що вказав на мою помилку, нище наведені оновленні результати.
Python 3.8.9 python3 -m timeit "dict(**{'a': 2, 'b': 4, 'c': 6, 'd': 8})" 1000000 loops, best of 5: 275 nsec per loop Python 3.9.3 python3 -m timeit "dict(**{'a': 2, 'b': 4, 'c': 6, 'd': 8})" 500000 loops, best of 5: 470 nsec per loop
Також для коротких ASCII строк пришвидшили `decode` метод так, наприклад приблизно на ~15% швидше:
Python 3.8.9 python3 -m timeit 'd={}; repr(d)' 2000000 loops, best of 5: 196 nsec per loop Python 3.9.3 python3 -m timeit 'd={}; repr(d)' 2000000 loops, best of 5: 179 nsec per loop
Але загалом це мало вплинуло на перфоманс, так зі сторінки патч ноута ми можемо прослідкувати за прогресом:
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 3.9 read_nonlocal 7.1 8.1 5.8 5.4 4.4 4.5 read_global 15.5 19.0 14.3 13.6 7.6 7.8 read_builtin 21.1 21.6 18.5 19.0 7.5 7.8 read_classvar_f_c 25.6 26.5 20.7 19.5 18.4 17.9 read_classvar_f_i 22.8 23.5 18.8 17.1 16.4 16.9 read_instancevar 32.4 33.1 28.0 26.3 25.4 25.3 read_instancevar_s 27.8 31.3 20.8 20.8 20.2 20.5 read_namedtuple 73.8 57.5 45.0 46.8 18.4 18.7 read_boundmethod 37.6 37.9 29.6 26.9 27.7 41.1 Variable and attribute write access: write_local 8.7 9.3 5.5 5.3 4.3 4.3 write_nonlocal 10.5 11.1 5.6 5.5 4.7 4.8 write_global 19.7 21.2 18.0 18.0 15.8 16.7 write_classvar 92.9 96.0 104.6 102.1 39.2 39.8 write_instancevar 44.6 45.8 40.0 38.9 35.5 37.4 write_instancevar_s 35.6 36.1 27.3 26.6 25.7 25.8 Data structure read access: read_list 24.2 24.5 20.8 20.8 19.0 19.5 read_deque 24.7 25.5 20.2 20.6 19.8 20.2 read_dict 24.3 25.7 22.3 23.0 21.0 22.4 read_strdict 22.6 24.3 19.5 21.2 18.9 21.5 Data structure write access: write_list 27.1 28.5 22.5 21.6 20.0 20.0 write_deque 28.7 30.1 22.7 21.8 23.5 21.7 write_dict 31.4 33.3 29.3 29.2 24.7 25.4 write_strdict 28.4 29.9 27.5 25.2 23.1 24.5 Stack (or queue) operations: list_append_pop 93.4 112.7 75.4 74.2 50.8 50.6 deque_append_pop 43.5 57.0 49.4 49.2 42.5 44.2 deque_append_popleft 43.7 57.3 49.7 49.7 42.8 46.4 Timing loop: loop_overhead 0.5 0.6 0.4 0.3 0.3 0.3
Висновки
Загалом, вводить декілька речей для Quality-of-Life, але найголовніше всередині з новим парсером, що дозволить нові фічі вже у версії 3.10.
Перед використанням важливо перевірити чи немає у вас функціоналу, що був видалений та речей що змінились
Так на сьогодні, остання версія — 3.9.4 яка вийшла 4 квітня, вже мала декілька bug-fixes та виправлених вразливостей.
42 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів