Adnotacje (Python3)

PEP 3107 - Function Annotations.

PEP 482 - Literature Overview for Type Hints.

PEP 484 - Type Hints.

PEP 526 - Syntax for Variable Annotations.

PEP 563 - Postponed Evaluation of Annotations.
Propozycja 'from __future__ import annotations' (Py3.7+). Adnotacje zmiennych i funkcji nie będą już obliczane w czasie definiowania zmiennych i funkcji, tylko będzie to odsunięte w czasie. W słowniku '__annotations__' będzie umieszczona postać napisowa adnotacji.

PEP 649 - Deferred Evaluation Of Annotations Using Descriptors.
Funkcje, klasy i moduły otrzymają nowy atrybut '__annotate__', który zawiera referencję do funkcji obliczającej adnotacje obiektu. Redefinicja atrybutu '__annotations__' jako 'data descriptor'.

https://docs.python.org/3/howto/annotations.html
Annotations Best Practices. Najlepsze praktyki dostępu do adnotacji zależą od wersji Pythona, ze względu na ciągły rozwój nowych narzędzi.

https://docs.python.org/3/library/inspect.html
'inspect' - Inspect live objects [Py3.5+]. There are four main kinds of services provided by this module: type checking, getting source code, inspecting classes and functions, and examining the interpreter stack. inspect.get_annotations() [Py3.10+].

https://docs.python.org/3/library/typing.html
'typing' - Support for type hints [Py3.5+].

https://typing.readthedocs.io/en/latest/spec/index.html
Specification for the Python type system.

WPROWADZENIE

Python wspiera adnotacje dla trzech różnych typów: funkcji, klas i modułów.

ADNOTACJE DLA FUNKCJI

Argumenty funkcji mogą mieć adnotacje (ang. annotations) postaci ': expression', następującą po nazwie parametru. Podobnie może być dla parametrów postaci *identifier lub **identifier. Funkcje mogą mieć również adnotację 'return' postaci '-> expression', która następuje po liście parametrów. Jest to składnia do dołączania do funkcji dodatkowych informacji (metadata) dotyczących parametrów i wartości zwracanych. Jest to mechanizm całkowicie opcjonalny. W Pythonie 2 można było tylko w docstringu coś dopowiedzieć.

Wartości adnotacji są dostępne jako wartości słownika '__annotations__', gdzie kluczami są nazwy argumentów funkcji (string).


# Syntax.

def nazwa_funkcji(arg1: wyrażenie, arg2: wyrażenie = wartość) -> wyrażenie:
   instrukcje

def nazwa_funkcji(*arguments: wyrażenie, **keywords: wyrażenie = wartość) -> wyrażenie:
   instrukcje

# Adnotacje funkcji są zwykle używane jako podpowiedzi dla typów.

def sum_two_numbers(a: int, b: float) -> complex:
   return complex(a + b)

sum_two_numbers.__annotations__
# {'a': <class 'int'>, 'b': <class 'float'>, 'return': <class 'complex'>}

# https://stackoverflow.com/questions/14379753/what-does-mean-in-python-function-definitions

def kinetic_energy(m: 'in kg', v: 'in m/s') -> 'Joules': 
    return 0.5 * m * v**2

# kinetic_energy.__annotations__ to słownik postaci
# {'m': 'in kg', 'v': 'in m/s', 'return': 'Joules'}

ADNOTACJE DLA KLAS


# Podczas użycia adnotacji zmiennej lub atrybutu klasy
# może wystąpić podstawienie wartości.

class C:
    field: int   #  adnotacja atrybutu klasy

    def __init__(self) -> None:   # konwencja dla konstruktora
        pass

# C.__annotations__ to słownik postaci
# {'field': <class 'int'>}

# Adnotacje zmiennych są zwykle używane jako podpowiedzi do typów.
import typing

count: int = 0   # adnotacja zmiennej i podstawienie

some_list: typing.List[int] = [3, 5]
some_list: list[int] = [3, 5]   # Py3.9+

some_dict: typing.Dict[str, float] = {"a": 1.2}
some_dict: dict[str, float] = {"a": 1.2}   # Py3.9+

coords: typing.Tuple[int, float] = (12, 3.4)
coords: tuple[int, float] = (12, 3.4)   # Py3.9+

Url = str   # alias typu

# Nazwy Dict, List, Set, FrozenSet używa się do wartości zwracanych.
# Dla argumentów funkcji preferuje się abstrakcyjne kolekcje typów, np.
# Mapping, Sequence, AbstractSet.

from typing import Sequence, Mapping, Set, Iterable, Iterator, Tuple, Text
from typing import TypeVar, Callable, Any, AnyStr
from typing import Union, Optional, Generic

T = TypeVar('T', int, float, complex)
Vector = Iterable[Tuple[T, T]]   # alias typu

def inproduct(v: Vector[T]) -> T:
    return sum(x*y for x, y in v)

# Callable[[Arg1Type, Arg2Type], ReturnType] ogólna postać
# Callable[[], str] bez argumentów, zwraca str
# Callable[[int, float], None] dwa argumenty (int i float), zwraca None

# Text to alias dla str (Py3) lub unicode (Py2)
# AnyStr jest zdefiniowane jako TypeVar('AnyStr', Text, bytes)
# Sequence[float]
# Set[int]
# Mapping[int, str]
# Mapping jest równowazne Mapping[Any, Any]
# Iterable jest równoważne Iterable[Any]
# Union[T1, T2, ...] ogólna postać, dopuszczalne typy to T1, T2, ...
# Union[int, float]
# Optional[T1] jest równoważne Union[T1, None]

Podpowiedzi do typów są i będą opcjonalne, nie są wymuszane w Pythonie. Bywają przydatne dla narzędzi do statycznej analizy typów (mypy, pytype).