Co to są adnotacje zmiennych?

84

Wkrótce zostanie wydany Python 3.6. PEP 494 - Harmonogram wydania Pythona 3.6 wspomina o końcu grudnia, więc przejrzałem Co nowego w Pythonie 3.6, aby zobaczyć, jak wspominają o adnotacjach zmiennych :

PEP 484 wprowadził standard dla adnotacji typu parametrów funkcji, czyli podpowiedzi typu. Ten PEP dodaje składnię do Pythona w celu dodawania adnotacji do typów zmiennych, w tym zmiennych klas i zmiennych instancji:

primes: List[int] = []

captain: str  # Note: no initial value!

class Starship:
     stats: Dict[str, int] = {}

Podobnie jak w przypadku adnotacji funkcji, interpreter Pythona nie przypisuje żadnego szczególnego znaczenia adnotacjom zmiennych, a jedynie przechowuje je w specjalnym atrybucie __annotations__klasy lub modułu. W przeciwieństwie do deklaracji zmiennych w językach z typami statycznymi, celem składni adnotacji jest zapewnienie łatwego sposobu określania metadanych typu strukturalnego dla narzędzi i bibliotek innych firm za pośrednictwem abstrakcyjnego drzewa składni i __annotations__atrybutu.

Tak więc z tego, co przeczytałem, są one częścią wskazówek dotyczących typów pochodzących z Pythona 3.5, opisanych w Co to są wskazówki dotyczące typów w Pythonie 3.5 .

Podążam za przykładem captain: stri class Starship, ale nie jestem pewien co do ostatniego: Jak to primes: List[int] = []wyjaśnić? Czy definiuje pustą listę, która będzie zezwalać tylko na liczby całkowite?

fedorqui 'SO przestań szkodzić'
źródło
10
Wskazówki dotyczące typów nie sprawdzają typów. primes: List[int] = []jest po prostu pustą listą jako primes = []. Różnica polega na tym, że twierdzisz, że primes ma zawierać tylko ints, a aplikacje innych firm mogą wpisać sprawdź swój program, aby zweryfikować to oświadczenie, ale kiedy uruchamiasz kod w dowolnym interpreterze Pythona, jest to to samo, co pisanie primes = [], a zatem primes: List[int] = []; primes.append("string")nadal ważny.
Bakuriu,
2
@Bakuriu tak, słuszna uwaga. Jak Jim Fasarakis-Hilliard opisuje w swojej odpowiedzi na temat Co to są wskazówki dotyczące typów w Pythonie 3.5 , dlaczego wskazówki dotyczące typówPomaga w sprawdzaniu typów, pomaga w tworzeniu dokumentacji i pomaga IDE w tworzeniu dokładniejszych i bardziej niezawodnych narzędzi . Zaczerpnięty z PEP 526 - Składnia adnotacji zmiennych , Python pozostanie językiem z dynamicznym typowaniem, a autorzy nigdy nie chcą, aby wskazówki dotyczące typów były obowiązkowe, nawet zgodnie z konwencją .
fedorqui „SO przestań szkodzić”
1
Czy to odpowiada na twoje pytanie? Co to są wskazówki dotyczące typów w Pythonie 3.5?
AMC

Odpowiedzi:

48

Wszystko pomiędzy :a =jest wskazówką dotyczącą typu, więc primesjest rzeczywiście zdefiniowane jako List[int]i początkowo ustawione na pustą listę (i statspoczątkowo jest pustym słownikiem, zdefiniowanym jako Dict[str, int]).

List[int]i Dict[str, int]nie są częścią kolejnej składni, jednak zostały one już zdefiniowane we wskazówkach dotyczących pisania w PEP w Pythonie 3.5. 3.6 PEP 526 - Składnia Zmienne adnotacji wniosku tylko definiuje składnię, aby dołączyć te same wskazówki do zmiennych; wcześniej można było tylko dołączać wskazówki dotyczące typów do zmiennych z komentarzami (np primes = [] # List[int].).

Oba typy Listi Dictsą typami ogólnymi , co oznacza, że ​​masz mapowanie listy lub słownika z określoną (konkretną) zawartością.

Ponieważ Lististnieje tylko jeden „argument” (elementy [...]składni), typ każdego elementu na liście. Bo Dictpierwszy argument to typ klucza, a drugi typ wartości. Zatem wszystkie wartości na primesliście są liczbami całkowitymi, a wszystkie pary klucz-wartość w statssłowniku są (str, int)parami, odwzorowującymi łańcuchy na liczby całkowite.

Zobacz definicje typing.Listi typing.Dict, rozdział dotyczący typów ogólnych , a także PEP 483 - Teoria wskazówek dotyczących typów .

Podobnie jak w przypadku wskazówek dotyczących typów funkcji, ich użycie jest opcjonalne i są również uważane za adnotacje (pod warunkiem, że istnieje obiekt, do którego można je dołączyć, czyli wartości globalne w modułach i atrybuty klas, ale nie lokalne w funkcjach), które można przeszukiwać za pomocą __annotations__atrybutu. Możesz dołączać dowolne informacje do tych adnotacji, nie jesteś ściśle ograniczony do wpisywania wskazówek.

Możesz przeczytać całą propozycję ; zawiera dodatkowe funkcje wykraczające poza nową składnię; określa, kiedy takie adnotacje są oceniane, jak je introspekować i jak na przykład zadeklarować coś jako atrybut klasy w porównaniu z atrybutem instancji.

Martijn Pieters
źródło
1
Czy mogę traktować wskazówki typu jako rodzaj komentarzy „do odczytu maszynowego”, ponieważ nie mają one wpływu na sposób działania kodu (z wyjątkiem obj.__annotations__atrybutu)?
iBug
2
@iBug: adnotacje to komentarze do odczytu maszynowego, o ile komentarze są i tak czytelne dla człowieka. :-)
Martijn Pieters
59

Co to są adnotacje zmiennych?

Adnotacje zmiennych to tylko kolejny krok po # typekomentarzach, tak jak zostały zdefiniowane w PEP 484; uzasadnienie tej zmiany podkreślono w odpowiedniej sekcji PEP 526 .

Więc zamiast podpowiadać typ za pomocą:

primes = []  # type: List[int]

Wprowadzono nową składnię, aby umożliwić bezpośrednie przypisanie typu adnotacji do formularza:

primes: List[int] = []

który, jak zauważył @Martijn, oznacza listę liczb całkowitych przy użyciu typów dostępnych w programie typingi inicjalizując ją jako pustą listę.

Jakie zmiany to przynosi?

Pierwszą wprowadzoną zmianą była nowa składnia, która umożliwia :przypisywanie do nazwy typu, samodzielnego po znaku lub opcjonalnie adnotacji z jednoczesnym przypisaniem do niej wartości:

annotated_assignment_stmt ::=  augtarget ":" expression ["=" expression]

A więc przykład o którym mowa:

   primes: List[int] = [ ]
#    ^        ^         ^
#  augtarget  |         |
#         expression    |
#                  expression (optionally initialize to empty list)

Dodatkowe zmiany zostały również wprowadzone wraz z nową składnią; moduły i klasy mają teraz __annotations__atrybut (tak jak funkcje miały od czasu PEP 3107 - Adnotacje funkcji ), w którym dołączone są metadane typu:

from typing import get_type_hints  # grabs __annotations__

Teraz __main__.__annotations__zawiera zadeklarowane typy:

>>> from typing import List, get_type_hints
>>> primes: List[int] = []
>>> captain: str
>>> import __main__
>>> get_type_hints(__main__)
{'primes': typing.List<~T>[int]}

captainobecnie nie jest wyświetlany, get_type_hintsponieważ get_type_hintszwraca tylko typy, do których można również uzyskać dostęp w module; tj. najpierw potrzebuje wartości:

>>> captain = "Picard"
>>> get_type_hints(__main__)
{'primes': typing.List<~T>[int], 'captain': <class 'str'>}

Użycie print(__annotations__)pokaże, 'captain': <class 'str'>ale naprawdę nie powinieneś mieć __annotations__bezpośredniego dostępu .

Podobnie dla zajęć:

>>> get_type_hints(Starship)
ChainMap({'stats': typing.Dict<~KT, ~VT>[str, int]}, {})

Gdzie a ChainMapjest używany do pobierania adnotacji dla danej klasy (znajdujących się w pierwszym odwzorowaniu) i wszystkich adnotacji zdefiniowanych w klasach bazowych znalezionych w jej mro(konsekwentne mapowania {}dla obiektu).

Wraz z nową składnią dodano nowy ClassVartyp oznaczający zmienne klasowe. Tak, statsw twoim przykładzie jest w rzeczywistości zmienną instancji , a nie ClassVar.

Czy będę zmuszony go użyć?

Podobnie jak w przypadku wskazówek dotyczących typów z witryny PEP 484, są one całkowicie opcjonalne i służą głównie do sprawdzania typów (i wszystkiego, co można zbudować na podstawie tych informacji). Jest to tymczasowe, gdy zostanie wydana stabilna wersja Pythona 3.6, więc w przyszłości mogą zostać wprowadzone drobne poprawki.

Dimitris Fasarakis Hilliard
źródło