Adnotări funcționale Python 3

Funcțiile adnotărilor sunt o caracteristică Python 3 care vă permite să adăugați metadate arbitrare pentru a funcționa argumentele și valoarea returnată. Acestea făceau parte din speculația originală Python 3.0.

În acest tutorial vă voi arăta cum să profitați de adnotările de funcții generale și să le combinați cu decoratorii. Veți afla, de asemenea, despre avantajele și dezavantajele adnotărilor de funcții, atunci când este adecvat să le utilizați și când este mai bine să utilizați alte mecanisme precum docstrings și decoratori simpli.

Funcții adnotări

Funcțiile adnotărilor sunt specificate în documentul PEP-3107. Principala motivație a fost aceea de a oferi o modalitate standard de asociere a metadatelor pentru a funcționa argumentele și valoarea returnată. O mulțime de membri ai comunității au găsit cazuri noi de utilizare, dar au folosit diferite metode, cum ar fi decoratori personalizați, formate personalizate docstring și adăugarea de atribute personalizate obiectului de funcție.

Este important să înțelegeți că Python nu binecuvântează adnotările cu nici o semantică. Acesta oferă pur și simplu un suport sintactic frumos pentru asocierea metadatelor, precum și o modalitate ușoară de accesare a acestora. De asemenea, adnotările sunt complet opționale.

Să aruncăm o privire la un exemplu. Iată o funcție foo () care ia trei argumente numite a, b și c și imprimă suma lor. Rețineți că foo () nu întoarce nimic. Primul argument A nu este adnotată. Al doilea argument b este adnotat cu șirul "adnotând b", iar al treilea argument c este adnotat cu tipul int. Valoarea returnată este adnotată cu tipul pluti. Rețineți sintaxa "->" pentru a adnota valoarea returnată.

(a, b: 'adnotarea b', c: int) -> float: print (a + b + c)

Adnotările nu au niciun impact asupra execuției funcției. Hai sa sunăm foo () de două ori: o dată cu argumente int și o dată cu argumente șir. În ambele cazuri, foo () are dreptate, iar adnotările sunt pur și simplu ignorate.

"python foo (" Bună ziua ",", "," Lumea! ") Bună ziua, Lumea!

foo (1, 2, 3) 6 "

Argumentele implicite

Argumentele prestabilite sunt specificate după adnotare:

"python def foo (x: 'un argument care implicit la 5' = 5): print (x)

foo (7) 7

foo () 5 "

Accesarea adnotărilor funcției

Obiectul funcției are un atribut numit "adnotări“. Este o mapare care hărți fiecare nume de argument în adnotarea sa. Adnotarea valorii returnate este mapată la cheia "return", care nu poate intra în conflict cu nici un nume de argument, deoarece "return" este un cuvânt rezervat care nu poate servi drept nume de argument. Rețineți că este posibil să treceți un argument de cuvânt cheie numit întoarcere la o funcție:

"python def bar (* args, ** kwargs: 'argumentele cuvintelor dict'): print (kwargs ['return'])

d = 'retur': 4 bar (** d) 4 "

Să ne întoarcem la primul exemplu și să verificăm adnotările sale:

"python def foo (a, b: 'adnotarea b', c: int) -> float: print (a + b + c)

imprimare (foo.adnotări) 'c': , "b": "adnotare b", "întoarcere":

Acest lucru este destul de simplu. Dacă adnotați o funcție cu o array de argumente și / sau cu argumente de cuvinte cheie, atunci evident nu puteți adnota argumentele individuale.

"python def foo (* args: 'listă de argumente anonime', ** kwargs: 'dict al argumentelor'): print (args, kwargs)

imprimare (foo.adnotări) 'args': 'listă de argumente anonime', 'kwargs': 'dict al argumentelor denumite' "

Dacă citiți secțiunea despre accesarea adnotărilor de funcții în PEP-3107, se spune că le accesați prin atributul "func_annotations" al obiectului de funcție. Acest lucru nu este actualizat din Python 3.2. Nu fi confuz. Este pur și simplu "adnotări".

Ce puteți face cu adnotările?

Aceasta este întrebarea mare. Adnotările nu au semnificație standard sau semantică. Există mai multe categorii de utilizări generice. Puteți să le utilizați ca o documentație mai bună și să mutați argumentația și returnarea documentației de valoare din docstring. De exemplu, această funcție:

(a, b): "" "Împărțiți după a b: a - dividendul b - divizorul (trebuie să fie diferit de 0) retur: rezultatul împărțirii a a b

Pot fi convertite în:

python def div (a: "dividendul", b: "divizorul (trebuie să fie diferit de 0)") -> "rezultatul împărțirii a cu b": " b

În timp ce aceleași informații sunt captate, există câteva avantaje pentru versiunea adnotărilor:

  1. Dacă redenumiți un argument, versiunea docstring a documentației poate să nu mai fie actualizată.
  2. Este mai ușor să vedem dacă un argument nu este documentat.
  3. Nu este nevoie să vină cu un format special de documentație argument în interiorul docstring pentru a fi analizat de instrumente. adnotări atributul oferă un mecanism de acces direct, standard.

O altă utilizare despre care vom vorbi mai târziu este tastarea opțională. Python este tastat dinamic, ceea ce înseamnă că puteți trece orice obiect ca argument al unei funcții. Dar, deseori funcțiile vor necesita argumente pentru a fi de un anumit tip. Cu adnotări, puteți specifica tipul exact alături de argument într-un mod foarte natural.

Amintiți-vă că doar specificarea tipului nu o va impune și va fi necesară o muncă suplimentară (o mulțime de muncă). Cu toate acestea, chiar specificarea tipului poate face intenția mai lizibilă decât specificarea tipului în docstring și poate ajuta utilizatorii să înțeleagă cum să apeleze funcția.

Cu toate acestea, un alt avantaj al adnotărilor față de docstring este că puteți atașa diferite tipuri de metadate ca noduri sau dictaturi. Din nou, puteți face acest lucru și cu docstring prea, dar va fi bazat pe text și va necesita o parsare specială.

În cele din urmă, puteți atașa o mulțime de metadate care vor fi folosite de instrumente externe speciale sau în timpul rulării prin decoratori. Voi explora această opțiune în secțiunea următoare.

Adnotări multiple

Să presupunem că doriți să adnotați un argument atât cu tipul său cât și cu un șir de ajutor. Acest lucru este foarte ușor cu adnotările. Puteți să adnotați pur și simplu argumentul cu un dict care are două chei: 'tip' și 'help'.

"dict (type = float, help =" divizorul (trebuie să fie diferit de 0) ") -> dict (type = float, float, help = "rezultatul împărțirii a cu b"): "" "Împărțiți a b" "" returnați a / b

imprimare (div.adnotări) 'help': 'dividend', 'type': float, 'b': 'help' , 'return': 'help': 'rezultatul împărțirii unui b', 'tip': float "

Combinând adnotările Python și decoratorii

Adnotările și decoratorii merg mână în mână. Pentru o introducere bună în decoratorii Python, verificați cele două tutoriale: Deep Dive Into Python Decorators și Scrieți propriile dvs. decoratori Python.

Mai întâi, adnotările pot fi implementate integral ca decoratori. Puteți defini doar o @adnota decorator și să ia un nume de argument și o expresie Python ca argumente și apoi să le stocheze în funcția țintă adnotări atribut. Acest lucru se poate face și pentru Python 2.

Cu toate acestea, puterea reală a decoratorilor este că pot acționa asupra adnotărilor. Aceasta necesită, bineînțeles, o coordonare a semanticii adnotărilor.

Să ne uităm la un exemplu. Să presupunem că vrem să verificăm dacă argumentele sunt într-un anumit interval. Adnotarea va fi o tuplă cu valoarea minimă și maximă pentru fiecare argument. Apoi avem nevoie de un decorator care să verifice adnotarea fiecărui argument al cuvântului cheie, să verifice dacă valoarea este în interval și să ridice excepția altfel. Să începem cu decoratorul:

python def check_range (f): def decorat (* args, ** kwargs): pentru nume, interval în f .__ adnotări __. elemente (): min_value, max_value = interval dacă nu (min_value <= kwargs[name] <= max_value): msg = 'argument is out of range [ - ]' raise ValueError(msg.format(name, min_value, max_value)) return f(*args, **kwargs) return decorated

Acum, să definim funcția noastră și să o decorăm cu @check_range decoratori.

(a: (0, 8), b: (5, 9), c: (10, 20)): returnați a * b - c

Hai sa sunăm foo () cu argumente diferite și să vedem ce se întâmplă. Când toate argumentele se încadrează în limitele lor, nu există nici o problemă.

python foo (a = 4, b = 6, c = 15) 9

Dar dacă setăm c la 100 (în afara intervalului (10, 20), atunci se ridică o excepție:

Python foo (a = 4, b = 6, c = 100) ValueError: argumentul c este în afara intervalului [10-20]

Când trebuie să utilizați decoratori în loc de adnotări?

Există mai multe situații în care decoratorii sunt mai buni decât adnotările pentru atașarea metadatelor.

Un caz evident este dacă codul dvs. trebuie să fie compatibil cu Python 2.

Un alt caz este dacă aveți o mulțime de metadate. După cum ați văzut mai devreme, în timp ce este posibil să atașați orice cantitate de metadate utilizând dictaturi ca adnotări, este destul de greoaie și de fapt dă lenebilitate.

În cele din urmă, dacă se presupune că metadatele vor fi operate de un anumit decorator, ar fi mai bine să asociați metadatele ca argumente pentru decoratorul în sine.

Adnotări dinamice

Adnotările sunt doar un atribut dict al unei funcții.

tip ptthon (foo .__ adnotări__) dict

Aceasta înseamnă că le poți modifica în timp ce rulează programul. Care sunt unele cazuri de utilizare? Să presupunem că doriți să aflați dacă o valoare implicită a unui argument este folosită vreodată. Ori de câte ori funcția este apelată cu valoarea implicită, puteți crește valoarea unei adnotări. Sau poate doriți să rezumați toate valorile returnate. Aspectul dinamic se poate face chiar în interiorul funcției sau de către un decorator.

"adăugați python def (a, b) -> 0: rezultat = a + b adăugați.adnotări['retur'] + = rezultatul rezultatului rezultat

imprimare (add.adnotări['retur']] 0

adăugați (3, 4) 7 tipăriți (adăugați.adnotări[return]] 7

adăugați (5, 5) 10 imprimare (adăugați.adnotări['întoarcere']) 17 "

Concluzie

Funcțiile adnotărilor sunt versatile și interesante. Ei au potențialul de a introduce o nouă eră de instrumente introspective care ajută dezvoltatorii să stăpânească sisteme tot mai complexe. De asemenea, oferă dezvoltatorului mai avansat o modalitate standard și lizibilă de a asocia metadatele direct cu argumentele și valoarea returnată pentru a crea instrumente personalizate și pentru a interacționa cu decoratorii. Dar este nevoie de ceva efort pentru a beneficia de ele și pentru a-și valorifica potențialul.

Aflați Python

Aflați Python cu ghidul nostru complet de instrucțiuni Python, indiferent dacă sunteți doar începători sau sunteți un coder experimentat în căutarea unor noi abilități.

Cod