Коробка с питоном
538 subscribers
45 photos
119 links
Заметки от Python-разработчика: сниппеты, обзоры пакетов, новости и другая полезная информация.

Автор: @kiriharu
Download Telegram
Коробка с питоном
Там у Python 3.11 сегодня релиз, пройдемся по изменениям. Разделю пост на 2 части, сначала расскажу про всё остальное, а потом про typing, который снова прокачали. 1) Теперь Питон быстрее на 10-60% чем предыдущая версия. Почему такой разброс и где что ускорилось…
Немного с запозданием, но поговорим про новые фичи в typing.

1) NotRequired и Required поля в TypedDict.
Есть такой специальный класс как TypedDict - он позволяет добавить тайпхинты к словарю.
Теперь для описания полей в нём добавили три вещи:
1. NotRequired - показывает, что данного поля может и не быть в словаре.
2. Required - показывает, что данное поле обязательно должно быть в словаре
3. Аргумент total - если его значение ложно, то все поля в этом классе будут отмечены как NotRequired. По умолчанию оно истинно.

Почитать подробнее о нём можно вот тут

2) Self.
Ликуйте, господа, мы дождались! Больше никаких костылей с TypeVar!
Аннотация Self показывает что мы хотим вернуть инстанс текущего класса. Выглядит это как-то так:

class MyLock:
def __enter__(self) -> Self:
self.lock()
return self

Простенько и лаконично. Вот PEP 673.

3) LiteralString.
Относительно этого тайпхинта строки бывают двух типов - литеральные строки и произвольные строки. К литеральным строкам относятся константы, к произвольным - всё остальное. Данный тип указывает, что строка должна быть первого типа, на остальное оно будет ругаться. Вот пример:

def run_query(sql: LiteralString) -> ...
...

def caller(
arbitrary_string: str,
query_string: LiteralString,
table_name: LiteralString,
) -> None:
run_query("SELECT * FROM students") # ok
run_query(query_string) # ok
run_query("SELECT * FROM " + table_name) # ok
run_query(arbitrary_string) # type checker error
run_query( # type checker error
f"SELECT * FROM students WHERE name = {arbitrary_string}"
)

Исходя из примера видно, что использоваться это будет во всяких функциях, которые выполняют, например, SQL запросы или CLI-утилитах не давая на уровне тайпчекера засунуть в функцию очередной rm -rf. А PEP 675 можно почитать здесь.

4) Data class transforms
Декоратор который декорирует класс, метакласс или функцию которые являются декоратором. Получившийся декоратор умеет показывать тайпчекеру что декорируемый объект имеет фичи как у датакласса. Причина создания такой штуки проста - нет стандартного пути обозначения, что какой-то объект имеет свойства, присущие датаклассу. Привет Pydantic! PEP 681 читать здесь.

5) Вариативные дженерики.
Есть такая штука как TypeVar - она позволяет создавать дженерик параметризированный одним типом. А если нам нужно параметризировать произвольное количество типов? Теперь появился TypeVarTuple, который решает эту проблему.

Зачем это нужно: мы все знаем, что всякие датасотонисты любят наш питон из-за обилия всяких инструментов по типу NumPy или Pandas. Вариативные дженерики позволяют параметризировать структуры, которые подобны массивам, что в свою очередь позволяет находить ошибки при операции с этими структурами тайпчекеру.
PEP 646 читать здесь.

#pep
Разговоры шли давно, но 9ого числа представили PEP 703, который предлагает сделать GIL опциональным. Прототип опубликован в репозитории nogil.

В качестве основания используется то, что Python используют для ML, а GIL - это серьезное препятствие для параллельных вычислений.
В этом PEP так же привели работу с PEP 684, который предлагает прекратить использовать GIL между несколькими интерпретаторами в процессе.

Как думаете, могут ли действительно убрать/сделать опциональным GIL?

#pep
В PEP 695 предлагают наконец переделать синтаксис для указания дженериков и ввести новый оператор для указания алиасов.

1) Что там с дженериками?
Например, если раньше было так:
_T_co = TypeVar("_T_co", covariant=True, bound=str)

class ClassA(Generic[_T_co]):
def method1(self) -> _T_co:
...

То сейчас предлагают сделать так:
class ClassA[T: str]:
def method1(self) -> T:
...

2) А что с алиасами?
Раньше алиасы типов записывались вот так:
_T = TypeVar("_T")
ListOrSet: TypeAlias = list[_T] | set[_T]
Сейчас предлагают сделать вот так:
type ListOrSet[T] = list[T] | set[T]


Если не ошибаюсь, то ждем в 3.12, PEP уже приняли.

#pep
Появился PEP 723, который предлагает "встраивать" pyproject.toml в однофайловые скрипты.
Предлагается добавить переменную __pyproject__, которая будет содержать в себе валидный TOML, описывающий метадату скрипта, в том числе как скрипт запускать и какие зависимости необходимы для запуска.

К примеру, вот так будет выглядеть скрипт, которому для работы нужна библиотека requests и питон 3.11 или выше:

__pyproject__ = """
[project]
requires-python = ">=3.11"
dependencies = [
"requests<3",
]
"""
import requests
resp = requests.get("https://peps.python.org/api/peps.json")
print(resp.json())

PEP прикольный, что-то такое есть в качестве экспериментального RFC в Rust. Из минусов хотел бы отметить то, что автоматическая установка зависимостей может привести к запуску нежелательного кода. Но решение банальное - перед тем как что-то запускать, проверяйте, что вы запускаете.

#pep
Тианголо (создатель FastAPI) предложил новый PEP 727, который позволяет стандартизировать механизм документирования параметров.

Причина проста - существует много псевдо-стандартов по форматированию параметров в docstring (например свои стандарты есть у numpy, гугла, много их!), но не все редакторы/IDE имеют возможность поддерживать эти "микроязыки". Он предлагает стандартизировать подход, используя существующий мощный инструмент в виде аннотаций типов.

Основное предложение - добавление в typing новой функции doc, которая принимает единственный параметр documentation. Ожидается, что эта функция будет использоваться вместе с Annotated.

А вот как это будет выглядеть:

def create_user(
lastname: Annotated[str, doc("The **last name** of the newly created user")],
firstname: Annotated[str | None, doc("The user's **first name**")] = None,
) -> Annotated[User, doc("The created user after saving in the database")]:
"""
Create a new user in the system, it needs the database connection to be already
initialized.
"""
pass

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

Выглядит интересно, но важно понимать - автор пытается создать ещё один стандарт. Поддержит ли его комьюнити и уйдет ли PEP в работу? Остается только гадать.

#pep
Пока я спал, руководящий совет языка принял PEP 703 (Making the Global Interpreter Lock Optional in CPython).

Кратко о том, о чём говорится в посте:

1. Руководящему совету ясно, что несмотря на все проблемы и недостатки потоков, nogil будет полезен для Python, так как позволит находить более масштабируемые решения.
2. В то же время, они не уверены, получится ли убрать GIL не сломав при этом обратную совместимость - всё же не хотелось бы терять десятилетия развития базы пакетов. Существующая пакетная экосистема - это одна из сильных сторон языка, как и простая интеграция библиотек на C c CPython.
3. Оценить влияние nogil без реализации сложно, поэтому nogil должен выпускаться в составе регулярных релизов и не обязательно он там должен быть по-умолчанию.
4. Это всё ещё не гарантированная история. Если что-то пойдет не так - от изменений откажутся. Развёртывание должно быть постепенным и наиболее плавным.
5. Выкатка будет происходить в 3 фазы, которые возможно изменятся:
- В первой фазе nogil сделают возможным таргетом при сборке, чтобы разработчики могли тестировать свои пакеты.
- Во второй фазе, когда изменения в API и ABI будут сформированы, а поддержка nogil от сообщества будет достаточной, nogil-сборку добавлят как "поддерживаемую, но не по умолчанию".
- В третьей фазе nogil-сборку сделают сборкой "по-умолчанию", а от gil-сборки будут отказываться.
6. При успешной реализации nogil, ожидается падение производительности на 10-15% в худшем случае.

#pep