Аннотации во благо

Игорь Стариков / idle sign

Видео выступления

Автор

История вопроса

PEP 3107 Аннотации функций

2006 год. Python 3.0
  • Привязка выражения на этапе компиляции
  • Необязательность
  • Нет требований к смыслу
  • В атрибуте __annotations__

PEP 484 Указание типов

2014 год. Python 3.5
  • Статическая и динамическая проверки
  • Облегчение реорганизации кода
  • Кодогенерация
  • Новый стандартный смысл для аннотаций

PEP 526
Синтаксис для аннотаций переменных

2016 год. Python 3.6

PEP 563
Отложенное вычисление аннотаций

2017 год. Python 3.7
  • from __future__ import annotations
  • 3.8 — напоминание, если нет импорта
  • 3.9 — строгое напоминание
  • 4.0 — включено безоговорочно

PEP 585 Соглашения для удобства пользования аннотациями типов

2019 год. Python 3.8
  • Импорты только для аннотаций
  • Разница между List и list и т.п.
  • Неожиданное в typing: ABCs, NamedTuple

Кому пожать руку


Юкка Лехтосало

Гвидо ван Россум

Лукаш Ланга

Иван Левкивский
и сотни других замечательных разработчиков

Любители старины

  • Обратное портирование
  • Деаннотация

Применение

Проверка типов

  • Статическая — mypy, pyre, pytype
    
                                        $ mypy myapp.py
                                        $ pyre check
                                    
  • Динамическая — typeguard
    
                                        from typeguard import check_argument_types
    
                                        def some_function(a: int, b: float, c: str):
                                            assert check_argument_types()
    
                                    

Усиление анализатора в IDE

Автодокументирование

sphinx-autodoc-typehints

                            def add_me(arg1):
                                """
                                :param int arg1:
                                :rtype: int
                                """
                                return arg1 + arg1


                            def add_me_new(arg1: int) -> int:
                                return arg1 + arg1
                        

Обработка аргументов для CLI

clizy


                            def command(req: str, *, opt1: int = 10, opt2: bool = False):
                                ...

                        

Мосты в другие языки

ctyped


                            lib.myfunc.argtypes = [c_char_p, c_int]
                            lib.myfunc.restype = c_char_p
                            myfunc = lib.myfunc

                            myfunc('some'.encode('utf-8'), 2019).decode('utf-8')

                            @lib.function
                            def myfunc(title: str, year: int) -> str:
                                ...

                            myfunc('some', 2019)


                        

Кодогенерация: PEP 557 Классы данных


                            from __future__ import annotations
                            from dataclasses import dataclass, field, asdict


                            @dataclass(frozen=True)
                            class MyClass:

                                my_num: int = 42
                                my_list: List[int] = field(default_factory=list)


                            my_cls = MyClass(1, my_list=[1, 2, 3])
                            asdict(my_cls)

                        

Схематизация

marshmallow-annotations


                            import attr
                            from marshmallow_annotations import AnnotationSchema

                            @attr.s
                            class Album:
                                id: int = attr.ib()
                                name: str = attr.ib()

                            class AlbumScheme(AnnotationSchema):

                                class Meta:
                                    target = Album
                                    register_as_scheme = True

                        

Схематизация. Новый уровень

pydantic


                            from datetime import datetime
                            from pydantic.dataclasses import dataclass

                            @dataclass
                            class User:

                                id: int
                                name: str = 'anonymous'
                                signup_ts: datetime = None

                            user = User(id='42', signup_ts='2032-06-21T12:00')
                            user.json()
                            user.dict()

                        
* быстрее чем: marshmallow — 1.9x; django-restful-framework — 16.0x

Инверсия управления, внедрение зависимости

любителям изощрений посвящается
auto-init


                            from auto_init import AutoInitContext

                            class Point:
                                x: int
                                y: int
                                z: int = None

                            ctx = AutoInitContext()
                            p: Point = ctx.get_instance(Point)
                            assert p.x == 0
                            assert p.y == 0
                            assert p.z is None

                        

Аннотации это …

  • Проверка типов
  • Усиление анализатора в IDE
  • Обработка аргументов для CLI
  • Мосты в другие языки
  • Кодогенерация
  • Схематизация
  • IoC, DI
  • Перегрузка функций, функции общего вида
  • Упаковка (маршалинг) для RPC
  • Описание моделей в ORM
  • < ваш вариант >

Ссылки

Вопросы?

idlesign   idlesign   Эти слайды можно найти тут — bit.ly/ist_010