Suggerirei di leggere PEP 483 e PEP 484 e di guardare questa presentazione di Guido su Type Hinting.
In breve : il suggerimento per tipo è letteralmente il significato delle parole, accenni al tipo di oggetto / i che si sta utilizzando .
A causa della natura dinamica di Python, dedurre o verificare il tipo di un oggetto utilizzato è particolarmente difficile. Questo fatto rende difficile per gli sviluppatori capire cosa sta succedendo esattamente nel codice che non hanno scritto e, soprattutto, per gli strumenti di controllo del tipo trovati in molti IDE [PyCharm, PyDev vengono in mente] che sono limitati dal fatto che non hanno alcun indicatore del tipo di oggetti. Di conseguenza ricorrono a provare a inferire il tipo con (come menzionato nella presentazione) un tasso di successo del 50% circa.
Per prendere due diapositive importanti dalla presentazione Suggerimenti sul tipo:
Perché digitare suggerimenti?
- Aiuta a controllare i tipi: indicando quale tipo vuoi che l'oggetto sia il controllo dei tipi può facilmente rilevare se, ad esempio, stai passando un oggetto con un tipo non previsto.
- Aiuta con la documentazione: una terza persona che visualizza il tuo codice saprà cosa ci si aspetta dove, ergo, come usarlo senza ottenerlo
TypeErrors
.
- Aiuta gli IDE a sviluppare strumenti più precisi e robusti: gli ambienti di sviluppo saranno più adatti a suggerire metodi appropriati quando conoscono il tipo di oggetto. Probabilmente lo hai sperimentato con qualche IDE ad un certo punto, colpendo il
.
e avendo pop-up metodi / attributi che non sono definiti per un oggetto.
Perché usare i controllori statici?
- Trova i bug prima : questo è evidente, credo.
- Più grande è il tuo progetto, più ne avrai bisogno : Ancora una volta, ha senso. Le lingue statiche offrono una solidità e un controllo che mancano alle lingue dinamiche. Più grande e complessa è l'applicazione, maggiore è il controllo e la prevedibilità (da un aspetto comportamentale) necessari.
- I team di grandi dimensioni stanno già eseguendo analisi statiche : immagino che questo verifichi i primi due punti.
Come nota di chiusura di questa piccola introduzione : questa è una funzione opzionale e, da quello che ho capito, è stata introdotta per trarre alcuni vantaggi dalla tipizzazione statica.
In genere non è necessario preoccuparsene e sicuramente non è necessario utilizzarlo (soprattutto nei casi in cui si utilizza Python come linguaggio di scripting ausiliario). Dovrebbe essere utile quando si sviluppano grandi progetti in quanto offre robustezza, controllo e capacità di debug aggiuntive .
Tipo Suggerimento con mypy :
Per rendere questa risposta più completa, penso che una piccola dimostrazione sarebbe adatta. Userò mypy
la libreria che ha ispirato i suggerimenti di tipo quando sono presentati nel PEP. Questo è scritto principalmente per chiunque si imbattesse in questa domanda e si chiedesse da dove cominciare.
Prima di farlo, lasciatemi ripetere quanto segue: PEP 484 non impone nulla; sta semplicemente impostando una direzione per le annotazioni delle funzioni e proponendo linee guida su come il controllo del tipo può / dovrebbe essere eseguito. Puoi annotare le tue funzioni e suggerire tutte le cose che vuoi; i tuoi script verranno comunque eseguiti indipendentemente dalla presenza di annotazioni perché Python stesso non le utilizza.
Ad ogni modo, come osservato nel PEP, i tipi di suggerimento dovrebbero generalmente assumere tre forme:
Inoltre, ti consigliamo di utilizzare i suggerimenti sul tipo insieme al nuovo typing
modulo introdotto in Py3.5
. In esso, molti (ulteriori) ABC (classi di base astratte) sono definiti insieme a funzioni di supporto e decoratori per l'uso nel controllo statico. La maggior parte ABCs
in collections.abc
sono inclusi, ma in un Generic
modulo al fine di consentire di sottoscrizione (attraverso la definizione di un __getitem__()
metodo).
Per chiunque sia interessato a una spiegazione più approfondita di questi, mypy documentation
è scritto molto bene e ha molti esempi di codice che dimostrano / descrivono la funzionalità del loro correttore; merita sicuramente una lettura.
Annotazioni di funzioni e commenti speciali:
Innanzitutto, è interessante osservare alcuni dei comportamenti che possiamo ottenere usando commenti speciali. # type: type
Commenti speciali possono essere aggiunti durante le assegnazioni di variabili per indicare il tipo di un oggetto se non si può dedurre direttamente. I compiti semplici sono generalmente facilmente deducibili, ma altri, come le liste (per quanto riguarda il loro contenuto), non possono.
Nota: se vogliamo utilizzare qualsiasi derivato di Containers
e dobbiamo specificare i contenuti per quel contenitore, dobbiamo usare i tipi generici dal typing
modulo. Questi supportano l'indicizzazione.
# generic List, supports indexing.
from typing import List
# In this case, the type is easily inferred as type: int.
i = 0
# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = [] # type: List[str]
# Appending an int to our list
# is statically not correct.
a.append(i)
# Appending a string is fine.
a.append("i")
print(a) # [0, 'i']
Se aggiungiamo questi comandi a un file e li eseguiamo con il nostro interprete, tutto funziona perfettamente e print(a)
stampa il contenuto dell'elenco a
. I # type
commenti sono stati scartati, trattati come semplici commenti che non hanno alcun significato semantico aggiuntivo .
Eseguendo questo con mypy
, d'altra parte, otteniamo la seguente risposta:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
Indica che un elenco di str
oggetti non può contenere un int
che, staticamente parlando, è valido. Ciò può essere risolto rispettando il tipo di a
e solo aggiungendo str
oggetti o modificando il tipo di contenuto di a
per indicare che qualsiasi valore è accettabile (eseguito in modo intuitivo List[Any]
dopo che Any
è stato importato da typing
).
Le annotazioni delle funzioni vengono aggiunte nel modulo param_name : type
dopo ogni parametro nella firma della funzione e viene specificato un tipo restituito utilizzando la -> type
notazione prima dei due punti della funzione finale; tutte le annotazioni sono memorizzate __annotations__
nell'attributo per quella funzione in un comodo modulo dizionario. Usando un esempio banale (che non richiede tipi extra dal typing
modulo):
def annotated(x: int, y: str) -> bool:
return x < y
L' annotated.__annotations__
attributo ora ha i seguenti valori:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
Se siamo un noobie completo, o abbiamo familiarità con i Py2.7
concetti e di conseguenza non siamo a conoscenza del TypeError
nascondiglio nel confronto di annotated
, possiamo eseguire un altro controllo statico, rilevare l'errore e salvarci qualche problema:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
Tra le altre cose, anche la chiamata alla funzione con argomenti non validi verrà catturata:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
Questi possono essere estesi praticamente a qualsiasi caso d'uso e gli errori rilevati vanno oltre le chiamate e le operazioni di base. I tipi che puoi verificare sono davvero flessibili e ho semplicemente dato un piccolo assaggio del suo potenziale. Uno sguardo al typing
modulo, ai PEP o ai mypy
documenti ti darà un'idea più completa delle capacità offerte.
File stub:
I file stub possono essere utilizzati in due diversi casi non reciprocamente esclusivi:
- È necessario digitare check un modulo per il quale non si desidera modificare direttamente le firme delle funzioni
- Vuoi scrivere moduli e avere il controllo del tipo ma vuoi anche separare le annotazioni dal contenuto.
I file stub (con estensione di .pyi
) sono un'interfaccia annotata del modulo che si sta creando / si desidera utilizzare. Contengono le firme delle funzioni che si desidera verificare con il corpo delle funzioni scartate. Per avere un'idea di ciò, dato un insieme di tre funzioni casuali in un modulo chiamato randfunc.py
:
def message(s):
print(s)
def alterContents(myIterable):
return [i for i in myIterable if i % 2 == 0]
def combine(messageFunc, itFunc):
messageFunc("Printing the Iterable")
a = alterContents(range(1, 20))
return set(a)
Possiamo creare un file stub randfunc.pyi
, in cui possiamo porre alcune restrizioni se lo desideriamo. Il rovescio della medaglia è che qualcuno che visualizza la fonte senza lo stub non otterrà realmente l'assistenza di annotazione quando cerca di capire cosa dovrebbe essere passato dove.
Ad ogni modo, la struttura di un file stub è piuttosto semplicistica: aggiungi tutte le definizioni delle funzioni con corpi vuoti ( pass
riempiti) e fornisci le annotazioni in base alle tue esigenze. Qui, supponiamo di voler lavorare solo con i int
tipi per i nostri Container.
# Stub for randfucn.py
from typing import Iterable, List, Set, Callable
def message(s: str) -> None: pass
def alterContents(myIterable: Iterable[int])-> List[int]: pass
def combine(
messageFunc: Callable[[str], Any],
itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
La combine
funzione fornisce un'indicazione del motivo per cui potresti voler utilizzare le annotazioni in un altro file, a volte ingombrano il codice e riducono la leggibilità (grande no-no per Python). Ovviamente potresti usare gli alias di tipo, ma a volte confonde più di quanto aiuti (quindi usali saggiamente).
Questo dovrebbe farti familiarizzare con i concetti di base dei suggerimenti di tipo in Python. Anche se lo strumento di verifica del tipo utilizzato è stato
mypy
, dovresti gradualmente iniziare a vederne altri pop-up, alcuni internamente negli IDE ( PyCharm ) e altri come moduli standard di Python. Proverò ad aggiungere ulteriori checker / pacchetti correlati nel seguente elenco quando e se li trovo (o se suggerito).
Dama che conosco :
- Mypy : come descritto qui.
- PyType : da Google, usa una notazione diversa da quella che raccolgo, probabilmente vale la pena dare un'occhiata.
Pacchetti / progetti correlati :
- typeshed: repository Python ufficiale contenente un assortimento di file stub per la libreria standard.
Il typeshed
progetto è in realtà uno dei posti migliori in cui puoi guardare per vedere come il suggerimento tipo potrebbe essere usato in un tuo progetto. Prendiamo ad esempio i __init__
dunders della Counter
classe nel .pyi
file corrispondente :
class Counter(Dict[_T, int], Generic[_T]):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T]) -> None: ...
Dove _T = TypeVar('_T')
viene utilizzato per definire le classi generiche . Per la Counter
classe possiamo vedere che non può prendere argomenti nel suo inizializzatore, ottenere un singolo Mapping
da qualsiasi tipo a un int
o prendere uno Iterable
di qualsiasi tipo.
Avviso : una cosa che ho dimenticato di menzionare è che il typing
modulo è stato introdotto a titolo provvisorio . Da PEP 411 :
Un pacchetto provvisorio può avere la sua API modificata prima di "laurearsi" in uno stato "stabile". Da un lato, questo stato offre al pacchetto i vantaggi di essere formalmente parte della distribuzione Python. D'altra parte, il team di sviluppo principale afferma esplicitamente che non vengono fatte promesse in merito alla stabilità dell'API del pacchetto, che potrebbe cambiare per la prossima versione. Sebbene sia considerato un risultato improbabile, tali pacchetti possono anche essere rimossi dalla libreria standard senza un periodo di ammortamento se le preoccupazioni relative alla loro API o manutenzione si dimostrano fondate.
Quindi prendi le cose qui con un pizzico di sale; Dubito che verrà rimosso o modificato in modi significativi, ma non si può mai sapere.
** Un altro argomento del tutto, ma valido nell'ambito dei suggerimenti di tipo PEP 526
: La sintassi per le annotazioni delle variabili è uno sforzo per sostituire i # type
commenti introducendo una nuova sintassi che consente agli utenti di annotare il tipo di variabili in semplici varname: type
istruzioni.
Vedi Cosa sono le annotazioni variabili in Python 3.6? , come accennato in precedenza, per una piccola introduzione su questi.