Cosa significa -> nelle definizioni delle funzioni Python?


477

Di recente ho notato qualcosa di interessante guardando le specifiche grammaticali di Python 3.3 :

funcdef: 'def' NAME parameters ['->' test] ':' suite

Il blocco opzionale 'freccia' era assente in Python 2 e non sono riuscito a trovare alcuna informazione riguardo al suo significato in Python 3. Si scopre che questo è Python corretto ed è accettato dall'interprete:

def f(x) -> 123:
    return x

Ho pensato che questa potrebbe essere una sorta di sintassi preliminare, ma:

  • Non posso testare xqui, poiché è ancora indefinito,
  • Indipendentemente da ciò che ho inserito dopo la freccia (ad esempio 2 < 1), non influisce sul comportamento della funzione.

Qualcuno potrebbe abituarsi a questa sintassi può spiegarlo?

Risposte:


376

È un'annotazione di funzione .

Più in dettaglio, Python 2.x dispone di docstring, che consentono di collegare una stringa di metadati a vari tipi di oggetti. Questo è incredibilmente utile, quindi Python 3 estende la funzione consentendo di collegare metadati a funzioni che descrivono i loro parametri e restituiscono i valori.

Non esiste un caso d'uso preconcetto, ma il PEP ne suggerisce diversi. Uno molto utile è quello di consentire di annotare i parametri con i tipi previsti; sarebbe quindi facile scrivere un decoratore che verifichi le annotazioni o costringa gli argomenti al tipo giusto. Un altro è consentire la documentazione specifica per parametro invece di codificarla nel docstring.


122
E le informazioni sono disponibili come .__annotations__attributo.
Martijn Pieters

8
Wow, mi mancava una vasta area di conoscenza - non solo restituire annotazioni di valore, ma anche annotazioni di parametri. Grazie mille :).
Krotton,

4
@Krotton Non posso biasimarti per averlo perso, è praticamente inutilizzato. Ho mai incontrato una sola libreria che li usava, ed è abbastanza oscuro.

5
E l' __annotations__attributo è un dizionario. La chiave returnè quella usata per recuperare il valore dopo la freccia.
Keith,

9
@delnan - probabilmente il motivo per cui è in gran parte inutilizzato è perché la maggior parte delle librerie di Python punta ancora a essere compatibile con python2.x. Mentre python3.x inizia a diventare più standard, potremmo vedere più di queste cose spuntare qua e là ...
mgilson

253

Queste sono annotazioni di funzioni coperte in PEP 3107 . In particolare, ->segna l'annotazione della funzione di ritorno.

Esempi:

>>> def kinetic_energy(m:'in KG', v:'in M/S')->'Joules': 
...    return 1/2*m*v**2
... 
>>> kinetic_energy.__annotations__
{'return': 'Joules', 'v': 'in M/S', 'm': 'in KG'}

Le annotazioni sono dizionari, quindi puoi farlo:

>>> '{:,} {}'.format(kinetic_energy(20,3000),
      kinetic_energy.__annotations__['return'])
'90,000,000.0 Joules'

Puoi anche avere una struttura di dati Python piuttosto che solo una stringa:

>>> rd={'type':float,'units':'Joules','docstring':'Given mass and velocity returns kinetic energy in Joules'}
>>> def f()->rd:
...    pass
>>> f.__annotations__['return']['type']
<class 'float'>
>>> f.__annotations__['return']['units']
'Joules'
>>> f.__annotations__['return']['docstring']
'Given mass and velocity returns kinetic energy in Joules'

In alternativa, è possibile utilizzare gli attributi di funzione per convalidare i valori chiamati:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        try: 
            pr=test.__name__+': '+test.__docstring__
        except AttributeError:
            pr=test.__name__   
        msg = '{}=={}; Test: {}'.format(var, value, pr)
        assert test(value), msg

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    _between.__docstring__='must be between {} and {}'.format(lo,hi)       
    return _between

def f(x: between(3,10), y:lambda _y: isinstance(_y,int)):
    validate(f, locals())
    print(x,y)

stampe

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>

87

Come indicato da altre risposte, il ->simbolo viene utilizzato come parte delle annotazioni delle funzioni. Nelle versioni più recenti di Python >= 3.5, tuttavia, ha un significato definito .

PEP 3107 - Le annotazioni delle funzioni descrivono le specifiche, definendo le modifiche grammaticali, l'esistenza func.__annotations__in cui sono archiviate e il fatto che il suo caso d'uso sia ancora aperto.

In Python 3.5, tuttavia, PEP 484 - Type Hints attribuisce un significato unico a questo: ->viene utilizzato per indicare il tipo restituito dalla funzione. Sembra anche che questo verrà applicato nelle versioni future, come descritto in Che cosa riguarda gli usi esistenti delle annotazioni :

Lo schema concepibile più veloce introdurrebbe l'ammortamento silenzioso delle annotazioni non di tipo suggerimento in 3.6, l'ammortamento completo in 3.7 e dichiarerebbe i suggerimenti di tipo come l'unico uso consentito delle annotazioni in Python 3.8.

(Enfasi mia)

Questo non è stato effettivamente implementato 3.6per quanto ne so, quindi potrebbe essere superato nelle versioni future.

Secondo questo, l'esempio che hai fornito:

def f(x) -> 123:
    return x

sarà vietato in futuro (e nelle versioni attuali sarà confuso), dovrebbe essere cambiato in:

def f(x) -> int:
    return x

per poter descrivere efficacemente quella funzione frestituisce un oggetto di tipo int.

Le annotazioni non vengono utilizzate in alcun modo dallo stesso Python, le popola e le ignora praticamente. Spetta a librerie di terze parti lavorare con loro.


65

Nel seguente codice:

def f(x) -> int:
    return int(x)

il -> intgiusto dice che f()restituisce un numero intero (ma non impone alla funzione di restituire un numero intero). Si chiama annotazione di ritorno e si può accedere come f.__annotations__['return'].

Python supporta anche le annotazioni dei parametri:

def f(x: float) -> int:
    return int(x)

: floatdice alle persone che leggono il programma (e alcune librerie / programmi di terze parti, ad esempio pylint) che xdovrebbero essere a float. Vi si accede come f.__annotations__['x'], e non ha alcun significato da solo. Vedere la documentazione per ulteriori informazioni:

https://docs.python.org/3/reference/compound_stmts.html#function-definitions https://www.python.org/dev/peps/pep-3107/


4

Questo significa il tipo di risultato restituito dalla funzione, ma può essere None.

È molto diffuso nelle librerie moderne orientate su Python 3.x.

Ad esempio, è presente nel codice della libreria panda-profiling in molti luoghi, ad esempio:

def get_description(self) -> dict:

def get_rejected_variables(self, threshold: float = 0.9) -> list:

def to_file(self, output_file: Path or str, silent: bool = True) -> None:
"""Write the report to a file.

"Questo significa il tipo di risultato restituito dalla funzione, ma può essere Nessuno." Può essere Nessuno o qualsiasi altro tipo.
Ebram Shehata,

2

def function(arg)->123:

È semplicemente un tipo restituito, in questo caso il numero intero non importa quale numero scrivi.

come Java :

public int function(int args){...}

Ma per Python (come ha detto Jim Fasarakis Hilliard ) il tipo restituito è solo un suggerimento , quindi suggerisce il ritorno ma consente comunque di restituire un altro tipo come una stringa ..


1
def f(x) -> 123:
    return x

Il mio riassunto:

  1. ->Viene semplicemente introdotto per consentire agli sviluppatori di specificare facoltativamente il tipo di ritorno della funzione. Vedi la proposta di miglioramento Python 3107

  2. Questa è un'indicazione di come le cose potrebbero svilupparsi in futuro man mano che Python verrà ampiamente adottato - un'indicazione per scrivere forte - questa è la mia osservazione personale.

  3. Puoi anche specificare tipi per argomenti. La specifica del tipo restituito delle funzioni e degli argomenti aiuterà a ridurre gli errori logici e a migliorare i miglioramenti del codice.

  4. Puoi avere espressioni come tipo di ritorno (sia a livello di funzione che di parametro) e puoi accedere al risultato delle espressioni tramite l'attributo 'return' dell'oggetto annotazioni . le annotazioni saranno vuote per il valore di espressione / ritorno per le funzioni in linea lambda.


Grazie per le correzioni.
Maz
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.