Was bedeutet -> in Python-Funktionsdefinitionen?

476

Ich habe kürzlich etwas Interessantes beim Betrachten der Grammatikspezifikation von Python 3.3 bemerkt :

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

Der optionale 'Pfeil'-Block fehlte in Python 2 und ich konnte keine Informationen bezüglich seiner Bedeutung in Python 3 finden. Es stellt sich heraus, dass dies korrektes Python ist und vom Interpreter akzeptiert wird:

def f(x) -> 123:
    return x

Ich dachte, dass dies eine Art Voraussetzungssyntax sein könnte, aber:

  • Ich kann hier nicht testen, xda es noch undefiniert ist,
  • Egal was ich nach dem Pfeil setze (zB 2 < 1), es hat keinen Einfluss auf das Funktionsverhalten.

Könnte jemand, der mit dieser Syntax vertraut ist, es erklären?

Krotton
quelle

Antworten:

375

Es ist eine Funktionsanmerkung .

Im Einzelnen verfügt Python 2.x über Dokumentzeichenfolgen, mit denen Sie eine Metadatenzeichenfolge an verschiedene Objekttypen anhängen können. Dies ist erstaunlich praktisch, daher erweitert Python 3 die Funktion, indem Sie Metadaten an Funktionen anhängen können, die deren Parameter und Rückgabewerte beschreiben.

Es gibt keinen vorgefassten Anwendungsfall, aber der PEP schlägt mehrere vor. Eine sehr praktische Möglichkeit besteht darin, Parameter mit ihren erwarteten Typen zu versehen. Es wäre dann einfach, einen Dekorateur zu schreiben, der die Anmerkungen überprüft oder die Argumente zum richtigen Typ zwingt. Eine andere Möglichkeit besteht darin, eine parameterspezifische Dokumentation zuzulassen, anstatt sie in die Dokumentzeichenfolge zu codieren.

Katriel
quelle
122
Und die Informationen sind als .__annotations__Attribut verfügbar .
Martijn Pieters
8
Wow, ich habe ein ziemlich breites Wissensgebiet verpasst - nicht nur Rückgabewerte, sondern auch Parameteranmerkungen. Vielen Dank :).
Krotton
4
@Krotton Ich kann dir nicht die Schuld geben, dass du es verpasst hast, es ist praktisch unbenutzt. Ich habe immer nur eine einzige Bibliothek getroffen, die sie benutzt, und es ist ziemlich dunkel.
5
Und das __annotations__Attribut ist ein Wörterbuch. Der Schlüssel returnist derjenige, mit dem der Wert nach dem Pfeil abgerufen wird.
Keith
9
@delnan - wahrscheinlich liegt der Grund dafür, dass es größtenteils nicht verwendet wird, darin, dass die meisten Python-Bibliotheken immer noch darauf abzielen, mit python2.x kompatibel zu sein. Wenn python3.x mehr zum Standard wird, tauchen hier und da möglicherweise mehr dieser Dinge auf ...
mgilson
252

Dies sind Funktionsanmerkungen, die in PEP 3107 behandelt werden . Insbesondere ->markiert das die Annotation der Rückgabefunktion.

Beispiele:

>>> 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'}

Anmerkungen sind Wörterbücher. Sie können dies also tun:

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

Sie können auch eine Python-Datenstruktur anstelle einer Zeichenfolge verwenden:

>>> 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'

Sie können auch Funktionsattribute verwenden, um aufgerufene Werte zu überprüfen:

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)

Druckt

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

Wie in anderen Antworten angegeben, wird das ->Symbol als Teil von Funktionsanmerkungen verwendet. In neueren Versionen von Python >= 3.5hat es jedoch eine definierte Bedeutung.

PEP 3107 - Funktionsanmerkungen beschreiben die Spezifikation, definieren die Grammatikänderungen, die Existenz, func.__annotations__in der sie gespeichert sind, und die Tatsache, dass der Anwendungsfall noch offen ist.

In Python 3.5hat PEP 484 - Type Hints jedoch eine einzige Bedeutung: ->Wird verwendet, um den Typ anzugeben, den die Funktion zurückgibt. Es scheint auch , wie dies in zukünftigen Versionen erzwungen werden , wie beschrieben Was derzeitige Verwendung von Anmerkungen :

Das am schnellsten denkbare Schema würde die stille Ablehnung von Annotationen ohne Typhinweise in 3.6 und die vollständige Ablehnung von Annotationen ohne Typ in 3.7 einführen und Typhinweise als die einzig zulässige Verwendung von Annotationen in Python 3.8 deklarieren.

(Hervorhebung von mir)

3.6Soweit ich das beurteilen kann, wurde dies noch nicht implementiert , sodass es möglicherweise zu zukünftigen Versionen kommt.

Demnach haben Sie das folgende Beispiel angegeben:

def f(x) -> 123:
    return x

wird in Zukunft verboten sein (und in aktuellen Versionen wird verwirrend sein), müsste es geändert werden zu:

def f(x) -> int:
    return x

Damit diese Funktion effektiv beschrieben werden kann, wird fein Objekt vom Typ zurückgegeben int.

Die Anmerkungen werden von Python selbst in keiner Weise verwendet, sondern werden so gut wie ausgefüllt und ignoriert. Es liegt an Bibliotheken von Drittanbietern, mit ihnen zu arbeiten.

Dimitris Fasarakis Hilliard
quelle
64

Im folgenden Code:

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

Das -> intJust sagt, dass f()eine Ganzzahl zurückgegeben wird (aber es zwingt die Funktion nicht, eine Ganzzahl zurückzugeben). Es wird als Rückgabeanmerkung bezeichnet und kann als abgerufen werden f.__annotations__['return'].

Python unterstützt auch Parameteranmerkungen:

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

: floatteilt Personen, die das Programm lesen (und einige Bibliotheken / Programme von Drittanbietern, z. B. Pylint), mit, xdass a float. Es wird als zugegriffen f.__annotations__['x']und hat für sich genommen keine Bedeutung. Weitere Informationen finden Sie in der Dokumentation:

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

MaxiMouse
quelle
4

Dies bedeutet, welche Art von Ergebnis die Funktion zurückgibt, aber es kann sein None.

Es ist in modernen Bibliotheken, die auf Python 3.x ausgerichtet sind, weit verbreitet.

Zum Beispiel gibt es im Code der Bibliothek Pandas-Profiling an vielen Stellen zum Beispiel:

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.
Vitalii
quelle
"Dies bedeutet die Art des Ergebnisses, das die Funktion zurückgibt, aber es kann None sein." Es kann None oder ein anderer Typ sein.
Ebram Shehata
2

def function(arg)->123:

Es ist einfach ein Rückgabetyp, in diesem Fall spielt die Ganzzahl keine Rolle, welche Zahl Sie schreiben.

wie Java :

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

Aber für Python (wie Jim Fasarakis Hilliard sagte) ist der Rückgabetyp nur ein Hinweis , also schlägt er die Rückgabe vor, erlaubt aber trotzdem, einen anderen Typ wie einen String zurückzugeben.

Mike D3ViD Tyson
quelle
1
def f(x) -> 123:
    return x

Meine Zusammenfassung:

  1. Es wird einfach ->eingeführt, um Entwickler dazu zu bringen, optional den Rückgabetyp der Funktion anzugeben. Siehe Python-Erweiterungsvorschlag 3107

  2. Dies ist ein Hinweis darauf, wie sich die Dinge in Zukunft entwickeln könnten, wenn Python ausgiebig übernommen wird - ein Hinweis auf starkes Tippen - dies ist meine persönliche Beobachtung.

  3. Sie können auch Typen für Argumente angeben. Durch Angabe des Rückgabetyps der Funktionen und Argumente können logische Fehler reduziert und Codeverbesserungen verbessert werden.

  4. Sie können Ausdrücke als Rückgabetyp haben (sowohl auf Funktions- als auch auf Parameterebene), und auf das Ergebnis der Ausdrücke kann über das Attribut 'return' des Annotationsobjekts zugegriffen werden . Anmerkungen sind für den Ausdrucks- / Rückgabewert für Lambda-Inline-Funktionen leer.

Maz
quelle
Vielen Dank für Korrekturen.
Maz