Аннотации и статическая типизация

Аннотации

(немного копипасты из ../12_MetaclassMatch

Базовая статья: О дисциплине использования аннотаций

Duck typing:

Однако:

Поэтому нужны указания о типе полей классов, параметрах и возвращаемых значений функций/методов и т. п. — Аннотации (annotations)

<!> Аннотации настолько отвязаны от реализации, что, например, получить доступ к собственным аннотациям из функции очень трудно, и получается «хрупкий» код, см. прошлое Д/З.

Составные и нечёткие типы

составные типы:

Статическая модель типизации

Модуль typing

⇒ Это тема для целого курса.

Кратко:

Пример: dataclasses — типизированные структуры, логика базируется на аннотациях

MyPy

Что показательно:

  1. Статическая типизация в Python очень активно развивается, достаточно посмотреть сводный What's New и поискать там «type» или «typing».

  2. три официальных блога Гвидо: The Mypy Blog, Neopythonic, The History of Python

Совпадение? Не думаю!™

Ещё раз: зачем аннотации?

http://www.mypy-lang.org: статическая типизация в Python by default (ну, почти… или совсем!)

На MyPy основано большинство дисциплин разработки и систем проверки кода в различных IDE.

Компиляция

Пример для mypyc:

Д/З

  1. Прочитать про
  2. EJudge: MetaCheck 'Метакласс с проверкой'

    Написать метакласс checker, с помощью которого при создании класса будет происходить два дополнительных действия:

    • Все аннотированные поля класса, имеющие заданные значения, будут проверяться на то, что значение соответствует аннотации-типу, и в случае несоответствия — инициироваться исключение TypeError

    • Для каждого неаннотированного поля, имеющего заданное значения числового типа, будет создаваться соответствующая аннотация

    Input:

       1 import inspect
       2 
       3 class C(metaclass=checker):
       4     a: str = "QQ"
       5     b = 2
       6     s = "QKRQ"
       7 
       8 print(inspect.get_annotations(C))
       9 
      10 try:
      11     class E(metaclass=checker):
      12         a: str = 1
      13 except TypeError:
      14     print("NOPE") 
    
    Output:

    {'a': <class 'str'>, 'b': <class 'int'>}
    NOPE
  3. EJudge: AnnoDoc 'Аннотации как документация'

    Написать декоратор annodoc(), которым можно декорировать классы и функции. Декоратор должен просматривать аннотации объекта и выбирать из них только такие, у которых вместо типа в аннотации используется строка. Эти строки (если они есть) надо добавлять в документацию объекта так:

    • (всегда) В начало строки документации — имя: (где имя — это поле .__name__ объекта)

      • ⇒ Если у объекта не было строки документации, она создаётся
    • В конец строки документации — Variable имя: аннотация-строка (поля класса, формальные параметры функции или метода)

    • В самый конец строки документации — Returns: аннотация-строка для возвращаемого значения

    Аннотированный объект следует просмотреть рекурсивно, и для каждого аннотированного указанным способом атрибута изменить строку документации с помощью annodoc(). Гарантируется, что в тестах эта рекурсия конечна.

    Input:

       1 @annodoc
       2 class C:
       3     """The class"""
       4     const: "constant" = 1
       5     var: "variable"
       6     undoc = 42
       7 
       8     def method(x: "parameter", y: int) -> "return value":
       9         return param
      10 
      11 print(C.__doc__)
      12 print(C().method.__doc__)
    
    Output:

    C:
    The class
    Variable const: constant
    Variable var: variable
    method:
    Variable x: parameter
    Returns: return value
  4. EJudge: StrictBubble 'Типизированная сортировка пузырём'

    (эту задачу надо сдавать в EJudge, но основные свойства решения там пока проверить нельзя) Написать функцию bubble(sequence: Sortable) -> Sortable, которая сортирует эелменты изменяемой последовательности и возвращает её в отсортированном виде, и type alias Sortable, задающий

    • изменяемую последовательность,
    • элементы которой поддерживают операцию сравнения.

    В результате приведённый пример должен проходить mypy --strict, компилироваться mypyc и выполняться из полученной библиотеки, а любая закомментированная строка из примера — вызывать ошибку проверки/компиляции.

    Input:

       1 from typing import cast
       2 
       3 c = [60, 66, 67, 64, 65, 68, 60, 63, 63, 67, 66, 66, 67, 64, 66, 68, 61, 67, 64, 65]
       4 for s in ( bubble(cast(Sortable, c)),
       5            bubble(list(map(float, c))),
       6            bubble(list(map(str, c))),
       7            bubble(list(map(list, map(str, c))))):
       8     print(*s)
       9 # bubble(list(map(complex, c)))
      10 # bubble(tuple(map(float, c)))   
    
    Output:

    60 60 61 63 63 64 64 64 65 65 66 66 66 66 67 67 67 67 68 68
    60.0 60.0 61.0 63.0 63.0 64.0 64.0 64.0 65.0 65.0 66.0 66.0 66.0 66.0 67.0 67.0 67.0 67.0 68.0 68.0
    60 60 61 63 63 64 64 64 65 65 66 66 66 66 67 67 67 67 68 68
    ['6', '0'] ['6', '0'] ['6', '1'] ['6', '3'] ['6', '3'] ['6', '4'] ['6', '4'] ['6', '4'] ['6', '5'] ['6', '5'] ['6', '6'] ['6', '6'] ['6', '6'] ['6', '6'] ['6', '7'] ['6', '7'] ['6', '7'] ['6', '7'] ['6', '8'] ['6', '8']

LecturesCMC/PythonIntro2023/33_StaticTyping (последним исправлял пользователь FrBrGeorge 2024-11-03 18:15:36)