Ist es möglich, eine Lambda-Funktion hint einzugeben?

Lesezeit: 7 Minuten

Benutzeravatar von Nathan Merrill
Nathan Merrill

Derzeit können in Python die Parameter und Rückgabetypen einer Funktion wie folgt typisiert werden:

def func(var1: str, var2: str) -> int:
    return var1.index(var2)

Dies zeigt an, dass die Funktion zwei Strings akzeptiert und eine ganze Zahl zurückgibt.

Diese Syntax ist jedoch sehr verwirrend mit Lambdas, die wie folgt aussehen:

func = lambda var1, var2: var1.index(var2)

Ich habe versucht, Typhinweise sowohl für Parameter als auch für Rückgabetypen einzugeben, und ich kann keinen Weg finden, der keinen Syntaxfehler verursacht.

Ist es möglich, eine Lambda-Funktion hint einzugeben? Wenn nicht, gibt es Pläne für Typhinweis-Lambdas, oder gibt es einen Grund (neben dem offensichtlichen Syntaxkonflikt), warum nicht?

  • Ich denke, dass der übliche Anwendungsfall für ein Lambda in einer anderen Funktion verschachtelt ist (im Gegensatz zu regulären Funktionen, die an einer Stelle definiert und an einer ganz anderen Stelle aufgerufen werden). Wenn Sie die Typen in dieser Funktion bereits kennen, sollte es (im Prinzip) ziemlich einfach sein, herauszufinden, welche Typen dieses Lambda erhalten wird. Darüber hinaus würde das Ändern der Syntax zur Unterstützung von Anmerkungen nur Ihr Lambda erstellen Schwerer lesen.

    – Mgilson

    20. November 2015 um 19:06 Uhr

  • Warum würden Sie das tun wollen? Die Lambda-Syntax ist für „Wegwerf“-Funktionen zur Verwendung in sehr eingeschränkten Kontexten vorgesehen, beispielsweise als die key Argument für die sorted eingebaut. Ich sehe wirklich keinen Sinn darin, in solch begrenzten Kontexten Typhinweise hinzuzufügen. Verwenden Sie außerdem PEP-526-Variablenanmerkungen, um Typhinweise hinzuzufügen lambda Völlig verfehlt den Punkt, IMHO. Der lambda Syntax soll definieren anonym Funktionen. Was ist der Sinn der Verwendung lambda und sofort an eine Variable binden? Benutz einfach def!

    – Luciano Ramalho

    30. März 2020 um 21:15 Uhr


Benutzeravatar von Martijn Pieters
Martijn Pieters

Sie können sozusagen in Python 3.6 und höher verwenden PEP 526-Variablenanmerkungen. Sie können die Variable, die Sie zuweisen, mit Anmerkungen versehen lambda Ergebnis mit dem typing.Callable generisch:

from typing import Callable

func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)

Dadurch werden die Typhinweisinformationen nicht an das Funktionsobjekt selbst angehängt, sondern nur an den Namespace, in dem Sie das Objekt gespeichert haben, aber das ist normalerweise alles, was Sie für Typhinweiszwecke benötigen.

Sie können stattdessen aber auch einfach eine Funktionsanweisung verwenden; der einzige Vorteil, dass a lambda Angebote ist, dass Sie eine Funktionsdefinition für einen einfachen Ausdruck setzen können innen ein größerer Ausdruck. Aber das obige Lambda ist nicht Teil eines größeren Ausdrucks, es ist immer nur Teil einer Zuweisungsanweisung, die es an einen Namen bindet. Das ist genau das, was ein def func(var1: str, var2: str): return var1.index(var2) Aussage erreichen würde.

Beachten Sie, dass Sie keine Anmerkungen machen können *args oder **kwargs Argumente entweder separat, wie die Dokumentation für Callable Zustände:

Es gibt keine Syntax, um optionale Argumente oder Schlüsselwortargumente anzugeben; Solche Funktionstypen werden selten als Callback-Typen verwendet.

Diese Beschränkung gilt nicht für a PEP 544 Protokoll mit einem __call__ Methode; Verwenden Sie dies, wenn Sie eine aussagekräftige Definition dessen benötigen, welche Argumente akzeptiert werden sollen. Sie benötigen Python 3.8 oder installiere das typing-extensions Projekt für einen Backport:

from typing_extensions import Protocol

class SomeCallableConvention(Protocol):
    def __call__(self, var1: str, var2: str, spam: str = "ham") -> int:
        ...

func: SomeCallableConvention = lambda var1, var2, spam="ham": var1.index(var2) * spam

Für die lambda Ausdruck selbst, können Sie keine Anmerkungen verwenden (die Syntax, auf der Pythons Typhinweise aufgebaut sind). Die Syntax ist nur verfügbar für def Funktionsanweisungen.

Aus PEP 3107 – Funktionsanmerkungen:

Die Syntax von lambda unterstützt keine Anmerkungen. Die Syntax von Lambda könnte geändert werden, um Anmerkungen zu unterstützen, indem Klammern um die Parameterliste gesetzt werden. Wie auch immer, es wurde entschieden diese Änderung nicht vorzunehmen, weil:

  • Es wäre eine inkompatible Änderung.
  • Lambdas sind sowieso kastriert.
  • Das Lambda kann jederzeit in eine Funktion geändert werden.

Sie können die Anmerkungen weiterhin direkt an das Objekt anhängen, die function.__annotations__ Das Attribut ist ein beschreibbares Wörterbuch:

>>> def func(var1: str, var2: str) -> int:
...     return var1.index(var2)
...
>>> func.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}
>>> lfunc = lambda var1, var2: var1.index(var2)
>>> lfunc.__annotations__
{}
>>> lfunc.__annotations__['var1'] = str
>>> lfunc.__annotations__['var2'] = str
>>> lfunc.__annotations__['return'] = int
>>> lfunc.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}

Natürlich helfen Ihnen dynamische Anmerkungen wie diese nicht, wenn Sie einen statischen Analysator über Ihre Typhinweise ausführen möchten.

  • Wenn die Antwort lautet func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2) warum ist dann nicht a besser antworten def func(var1: str, var2: str) -> int: return var1.index(var2)???

    – Guido van Rossum

    5. Juli 2020 um 22:47 Uhr


  • @GuidovanRossum: weil das die Antwort auf a ist anders Frage. 🙂 Die Frage hier war, wie man Typhinweise auf ein Lambda anwendet, das das gleiche Ergebnis liefert wie das Definieren einer Funktion. Trotzdem füge ich einen Abschnitt hinzu, in dem erläutert wird, warum Sie möglicherweise kein Lambda verwenden möchten.

    – Martijn Pieters

    6. Juli 2020 um 20:02 Uhr

  • Nein, wörtlich genommen sollte die Antwort lauten: “Nein, es ist nicht möglich, es gibt keine Pläne, dies zu ändern, und die Gründe sind hauptsächlich syntaktischer Natur – und es ist einfach genug, eine benannte Funktion zu definieren und diese zu kommentieren.” Ich stelle auch fest, dass die vorgeschlagene Problemumgehung (Erstellen einer Variablen mit einem bestimmten aufrufbaren Typ) nicht sehr hilfreich ist, wenn das Lambda in einer großen Aufruf- oder Datenstruktur vergraben ist, also ist es in keiner Weise “besser” als das Definieren einer Funktion. (Ich würde behaupten, dass es so ist schlechter weil die Callable-Notation nicht sehr lesbar ist.)

    – Guido van Rossum

    7. Juli 2020 um 21:21 Uhr


  • @GuidovanRossum: Darf ich Sie einladen, dann Ihre eigene Antwort zu schreiben? Mit Ihrem spezifischen Status als Vater der Sprache würde eine Antwort von Ihnen diese mit der Zeit leicht übertreffen, und Sie können genau das schreiben, was Ihrer Meinung nach darin enthalten sein sollte.

    – Martijn Pieters

    8. Juli 2020 um 21:14 Uhr

  • Um eine typisierte Funktionsvariable zusammen mit einem Standardwert (vielleicht einem Noop) zu definieren, halte ich die obige Antwort für vernünftigen (wenn auch hässlichen, dank Callable-Syntax) Code

    – Abraham

    21. April 2021 um 21:37 Uhr


jans Benutzeravatar
jan

Seit Python 3.6 können Sie (siehe PEP 526):

from typing import Callable
is_even: Callable[[int], bool] = lambda x: (x % 2 == 0)

  • Ich verstehe diese Antwort nicht: wo stellen Sie den Typ ein x?

    – Stenci

    19. März 2018 um 21:07 Uhr

  • @stenci Das is_even Funktion ist ein Callable das erwartet einen int Argument, also ist x ein int.

    – Jan

    20. März 2018 um 13:58 Uhr

  • Ich verstehe jetzt. Auf den ersten Blick dachte ich, Sie kommentieren nur den vom Lambda zurückgegebenen Typ, nicht den Typ seiner Argumente.

    – Stenci

    20. März 2018 um 14:11 Uhr

  • Dies ist nicht wirklich das Annotieren des Lambda selbst: Sie können diese Annotationen nicht aus dem Lambda-Objekt abrufen, wie Sie dies für eine annotierte Funktion können

    – cz

    11. Juni 2018 um 12:05 Uhr

  • mypy 0.701 mit Python 3.7 überprüft dies korrekt: is_even('x') verursacht einen Typfehler.

    – Konrad Rudolf

    24. Mai 2019 um 13:02 Uhr

Für diejenigen, die beim Schreiben Ihres Codes nur einen schnellen Zugriff auf Intellisense suchen, ist ein Fast-Hack das Richtige Typ-kommentieren Sie den Parameter direkt vor der Lambda-Deklarationmach deine Arbeit und nur dann schattieren Sie es mit dem Parameter.

    x: YourClass
    map(lambda _ :  x.somemethod ...) # x has access to methods defined on YourClass

Dann gleich danach:

    x: YourClass # can remove or leave
    map(lambda x:  x.somemethod, ListOfYourObjects) # inner x now shadows the argument

Ich mag es nicht, einem Bezeichner eine Lambda-Funktion zuzuweisen (siehe Ist es pythonisch: Lambdas benennen). Wenn Sie das trotzdem tun möchten, haben Sie hier bereits Antworten.

Wenn Sie dies nicht tun und nur wollen, dass die Typprüfer ihr Ding machen, lautet die Antwort typing.cast:

from typing import cast, Callable

cast(Callable[[int], int], lambda x: x + 1)("foo")
# error: Argument 1 has incompatible type "str"; expected "int"

das funktioniert bei mir gut…

def fun()->None:
    def lam(i: int)->None:
        print("lam!", i)
    print("fun!")
    lam(1)
    lam(2)


fun()
lam()

Drucke

fun!
lam! 1
lam! 2
Traceback (most recent call last):
  File "/home/jail/prog.py", line 16, in <module>
    lam()
NameError: name 'lam' is not defined

getestet auf CPython 3.6.12 und 3.10.2

  • Hier gibt es keine Lambda-Funktionen.

    – Andrej

    26. Juli 2022 um 8:37 Uhr

  • Hier gibt es keine Lambda-Funktionen.

    – Andrej

    26. Juli 2022 um 8:37 Uhr

1443230cookie-checkIst es möglich, eine Lambda-Funktion hint einzugeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy