Программы
Обзор изменений в Python 3.7

Обзор изменений в Python 3.7

На днях вышла новая версия Python – почти 1.5 года разработки. Посмотрим, что же подвезли нам в этот раз.

На днях вышла новая версия Python – почти 1.5 года разработки. Посмотрим, что же подвезли нам в этот раз.

Новое C API для работы с thread local и в этот же пункт contextvars - context local переменные. Суть довольно простая: есть у нас глобальные переменные, которые зависят (могут быть модифицированы) от текущего треда – используем

static __thread char *p;

– само собой, зависит от платформы и т.д. Но так мы привязываем переменную к треду, в котором она используется. Классический пример – errno – глобальная переменная, в которой лежит последняя ошибка. Будет обидно, если один тред перетрёт эту информацию для другого. И, есть сильное подозрение, что новое C API было сделано как раз в рамках добавления contextvars.

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

Поэтому было бы неплохо положить подобную переменную в конкретный context storage, получать её, например по номеру текущего контекста. А лучше – автоматом получать номер контекста, а по нему – нужный экземпляр переменной. Именно это и сделали.

Итог: довольно приятное обновление – можно проще писать конкурентный код, работающий с общими ресурсами. Пожалуй, самое важное изменение этой версии.

Новый формат .pyc файлов – теперь помимо времени модификации файла используется хеш от оригинального файла. В общем, вторая извечная проблема – синхронизации кешей наконец-то достала разработчиков. Но не совсем – всё равно есть режим совместимости, есть разные режимы, позволяющие всё также не проверять при подгрузке на лету.

Итог: будет меньше обидных косяков для новичков. Остальные могут выкинуть алиас pyclean:

pyclean () {
  find . -type f -name "*.py[co]" -delete
  find . -type d -name "__pycache__" -delete
}

– хотя, я бы не торопился.

Добавлен модуль importlib.resources – попытка стандартизировать работу с файлами ресурсов. На данный момент получение доступа к файлам ресурсов происходит чаще всего с помощью __file__ – адресация относительно текущего файла (исходя из личных наблюдений).

Также существует более "правильный" вариант – setuptools.pkg_resources. Он более медленный и вероятность увидеть его использование довольно небольшая.

Есть ещё вариант захардкодить пути... Мы не будем даже о нём говорить.

Так вот, теперь можно получить файл из нужного пакета. Например:

example/
│
├── alice_in_wonderland.txt
└── __init__.py

– тогда мы можем получить доступ к ресурсу "alice_in_wonderland.txt", лежащему в пакете "example" следующим образом:

>>> from importlib import resources
>>> with resources.open_text("example", "alice_in_wonderland.txt") as fid:
...     alice = fid.readlines()
... 
>>> print("".join(alice[:7]))

Важны тут абстракции пакета (определён как Union[str, ModuleType]) и ресурса (определён как Union[str, os.PathLike]). То есть путь до ресурса пишем в виде пути от корня пакета.

Это также решает проблему с пакетам, которые хранятся в виде zip-файлов, где __file__ нам уже не поможет.

Итог: ещё один способ прочитать файл... Далеко не факт, что программисты откажутся от интуитивно понятного __file__.

Работа над системой типов

Ускорили typing, добавили generic типы и добавили отложенное исполнение аннотаций. Ускорение – всегда приятно, но к новшествам сложно отнести. Отложенное исполнение аннотаций – приятный "фикс" синтаксиса. Сравним 2 кода:

class C:
    @classmethod
    def from_string(cls, source: str) -> 'C':
        ...

и

class C:
    @classmethod
    def from_string(cls, source: str) -> C:
        ...

– да, "просто" убрали кавычки, но на один раздражающий момент меньше.

Напомню, что раньше нельзя было ссылаться на тип, который ещё не определён. А в случае с описанием методов класса это был довольно распространённый способ применения.

Однако, не всё так радужно – использовать это можно только включив нужную прагму:

from __future__ import annotations

– проблемы с совместимостью.

Обобщённые типы (generic) – вполне логичное развитие идей типизации. Если нам нужно написать код, который единообразно работать с разными типами, к тому же он не зависит от их контрактов, можно абстрагироваться от типов:

from typing import Sequence, TypeVar

T = TypeVar('T')      # Declare type variable

def first(l: Sequence[T]) -> T:   # Generic function
    return l[0]

Как и прочие аннотации, они не бьют нам по рукам, если мы делаем что-то неправильно. Но можно использовать средства IDE для более качественного анализа кода.

Итог: да, система типов развивается, но ожидать чего-то революционного не стоит. Разве что в Python 4. А пока это скорее "бижутерия" – дешёвый способ сделать красиво. Ну хоть так!

Менее значимые изменения

__getattr__ для модулей – развитие идей "всё есть объект" и динамики во все поля. Ещё больше возможностей писать непонятный код и разную магию.

Декоратор @dataclass – уже имеющийся collections.namedtuple, но с человеческим лицом.

Новая функция breakpoint() – вставить точку останова для дальнейшей отладки. По сути – алиас к

import pdb
pdb.set_trace()

Функции time с поддержкой наносекунд:

time.clock_gettime_ns()
time.clock_settime_ns()
time.monotonic_ns()
time.perf_counter_ns()
time.process_time_ns()
time.time_ns()

В классы str, bytes и bytearray добавлен метод isascii() для проверки строки на наличие только ASCII-символов.

Оптимизации

  • Снижены накладные расходы при вызове многих методов из стандартной библиотеки.
  • В целом методы теперь вызываются на 20% быстрее.
  • Время запуска самого Python снижено на 10-30%.
  • Импортирование typing теперь быстрее в 7 раз.
  • Ряд оптимизаций asyncio.
Также может быть вам интересно:

Решение проблемы с isskin.dll в Wine в MacOS и Linux

Лечим довольно распространённую ошибку при запуске в Wine

Читать »

Как в Ubuntu отключить уведомления об ошибках

С версии 12.04 в Ubuntu появился механизм уведомлений об ошибках... Да, как в Windows

Читать »
Фото Как установить PostgreSQL на Linux и создать базу и пользователя

Как установить PostgreSQL на Linux и создать базу и пользователя

PostgreSQL - система управления базой данных общего назначения. Одна из самых распространённых баз данных, используемая на многих коммерческих и некоммерческих проектах.

Фото Как сделать свою middleware в Django (с примерами)

Как сделать свою middleware в Django (с примерами)

Middleware или "промежуточное программное обеспечение" - элегантный способ установить общие правила обработки запросов и ответов приложения. Давайте напишем парочку middleware, чтобы понять, как они работают.

Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

Письма об ошибках, отчёты на почту, восстановление паролей - всё это полезно при работе с сайтом. Django предоставляет удобный способ это сделать с минимумом настроек!

Фото Добавляем поддержку медиа-файлов в Django проект

Добавляем поддержку медиа-файлов в Django проект

Современные сайты редко ограничиваются только текстом и вёрсткой. Часто в заметках красуются фотографии, а рядом с описанием товаров - их изображения.

Фото Новый оператор match-case в Python

Новый оператор match-case в Python

В новой версии Python (3.10) появится новый оператор. Новый оператор сопоставления по шаблону (match-case).

Фото Нет слов, одни... однострочники

Нет слов, одни... однострочники

На днях вышел пост со списком полезных однострочников для JavaScript программистов. Памятуя Perl-овую молодость, заглянул туда.

Фото Microsoft открывает исходники, а её IDE супер-популярна

Microsoft открывает исходники, а её IDE супер-популярна

Решил сложить пару фактов и немного над этим поразмыслить. Реально ли Microsoft "переобулись"?

Фото Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

В Django вы можете передавать данные в шаблоны посредством контекстов. Контекст передаётся из контроллера (view в терминах Django), однако, если одни и те же данные нужны в разных местах, лучше сделать свой контекст-процессор.