Wie kann ich nicht verwendete Funktionsargumente bezeichnen?

75

Wenn ich ein Tupel "dekonstruiere", kann ich damit _Tupelelemente bezeichnen, an denen ich nicht interessiert bin, z

>>> a,_,_ = (1,2,3)
>>> a
1

Wie kann ich mit Python 2.x dasselbe mit Funktionsargumenten ausdrücken? Ich habe versucht, Unterstriche zu verwenden:

>>> def f(a,_,_): return a
...
  File "<stdin>", line 1
SyntaxError: duplicate argument '_' in function definition

Ich habe auch versucht, das Argument ganz wegzulassen:

>>> def f(a,,): return a
  File "<stdin>", line 1
    def f(a,,): return a
        ^
SyntaxError: invalid syntax

Gibt es einen anderen Weg, um dasselbe zu erreichen?

Frerich Raabe
quelle
4
Warum nicht einfach Standardwerte für Ihre Argumente haben? Warum hätten Sie unbenutzte Argumente in einer Funktion?
Jamylak
5
@jamylak: Ich verwende ein Framework, das erwartet, dass ich Callables an verschiedenen Stellen weitergebe. In vielen Fällen benötige ich jedoch nicht alle Argumente, die vom Framework übergeben werden.
Frerich Raabe
5
@FrerichRaabe Wenn das Framework bestimmte Argumente erfordert, erscheint es am saubersten, nur dieselben Namen zu verwenden, die das Framework für jedes Argument definiert, unabhängig davon, ob die Funktion sie verwendet oder nicht. Das bedeutet auch, dass Sie die Signatur Ihrer Funktion nicht ändern müssen, wenn Sie später feststellen, dass Sie die anderen Argumente doch benötigt haben.
Duncan
2
@jamylak Für einen Kollegen (und Tools wie pylint) ist jedoch nicht klar, ob die Variablen absichtlich nicht verwendet werden. Sie würden also am Ende eine Art Kommentar hinzufügen - ich möchte dies nach Möglichkeit vermeiden, indem ich dies im Code selbst ausdrücke.
Frerich Raabe
1
@jamylak Ich habe mich nicht für die Antwort ausgesprochen, die ich verwenden wollte del(ich mag es auch nicht selbst), ich habe mich dagegen ausgesprochen, den Argumenten plausible Namen zu geben, aber sie dann einfach nicht zu verwenden, da Tools und Kollegen dies wahrscheinlich tun werden heben Sie ihre (elektronischen) Augenbrauen darüber. Natürlich kann ich meine Absichten immer als englischen Text dokumentieren, aber ich würde etwas bevorzugen, das es in Python ausdrückt.
Frerich Raabe

Antworten:

30

Folgendes mache ich mit unbenutzten Argumenten:

def f(a, *unused):
    return a
Fred Foo
quelle
2
Hm, schön! Funktioniert sogar mit Lambdas. Was tun, wenn ich das erste und dritte Argument ignorieren möchte, aber nicht das zweite?
Frerich Raabe
1
@FrerichRaabe: dann musst du noch den ersten ausschreiben. Das tut mir leid.
Fred Foo
Aha. Schade, in meinem Fall sind es tatsächlich die ersten (oder ersten beiden) Argumente, die ich ignorieren möchte. Trotzdem ist Ihr Ansatz gut zu wissen, danke für das Teilen! +1 von mir.
Frerich Raabe
2
@ JoelCornett: Ich bin nicht sicher, ob ich es verstehe. Vielleicht liegt es an meiner mangelnden Python-Expertise - ich arbeite mit einem Framework, bei dem ich ein Callable mit vier Argumenten übergeben muss. Das Framework ruft schließlich meine Funktion wie auf f(a,b,c,d). Es scheint die Argumente nicht explizit zu benennen - würde die Verwendung kwargsauf meiner Seite in diesem Fall helfen? Vielleicht ist das Framework nicht optimal, aber was soll ich sagen - ich muss das verwenden, was mir gegeben wurde. : -}
Frerich Raabe
1
@ JoelCornett: Das mache ich gerade. Es ist nur so, dass einige Argumente ziemlich lange Namen haben, also habe ich darüber nachgedacht, sie auf etwas zu verkürzen - sagen wir _ -, damit ich sehen kann, dass eine Variable übergeben wird - ich brauche sie einfach nicht (gerade jetzt).
Frerich Raabe
104

Eine lustige Art, an die ich gerade gedacht habe, ist das Löschen der Variablen:

def f(foo, unused1, unused2, unused3):
    del unused1, unused2, unused3
    return foo

Dies hat zahlreiche Vorteile:

  • Die nicht verwendete Variable kann weiterhin verwendet werden, wenn die Funktion sowohl als Positionsargument als auch als Schlüsselwortargument aufgerufen wird.
  • Wenn Sie es später verwenden, können Sie es nicht, da es gelöscht wurde, sodass das Risiko von Fehlern geringer ist.
  • Es ist die Standard-Python-Syntax.
  • PyCharm macht das Richtige! (Ab 2020 macht PyCharm nicht mehr das Richtige :( Verfolgen Sie dies unter https://youtrack.jetbrains.com/issue/PY-39889 )
  • PyLint wird sich nicht beschweren und die Verwendung delist die im PyLint-Handbuch empfohlene Lösung .
verpackt
quelle
2
Ich mag den Tipp sehr und werde ihn übernehmen. Ich würde immer noch einen kleinen Kommentar hinzufügen, zum Beispiel, # Ignored parameters.da die Absicht nicht wirklich darin besteht, die Variablen zu "löschen".
Teebrin
6
Großartig, dies ist die EINZIGE Antwort, die sich mit dem FÜHREN nicht verwendeter Parameter im Gegensatz zum Schleppen befasst. Die anderen sollten herabgestimmt oder gelöscht werden;)
Tomasz Gandor
1
Erkennt der Optimierer dies?
Pepedou
1
@Pepedou Wahrscheinlich momentan nicht im typischen Fall. Das delselbst kümmert sich darum, das Argument "freizugeben" (so dass es für die Speicherbereinigung in Frage kommt, wenn nichts anderes darauf verweist). Der einzige andere Aufwand besteht darin, dass das Argument selbst übergeben wird. Dies sollte vernachlässigbar sein, kann jedoch nicht ohne Analyse des gesamten Programms optimiert werden. 1) Finden und Ändern jedes Aufrufers, um diese Argumente nicht zu übergeben. 2) Sicherstellen, dass diese optimierten Aufrufer keine Funktionen aufrufen , das kann noch diese Argumente in dieser optimierten Art und Weise verwenden, und 3) zu beweisen , dass keine andere Aufrufer der Funktion vorhanden ist . (Fortsetzung)
mtraceur
1
@Pepedou (Forts.) Aber Python ist so dynamisch, dass die dritte Anforderung im allgemeinen Fall grundsätzlich unmöglich ist. Ein "ausreichend fortgeschrittener hypothetischer Optimierer" könnte Code erstellen, der einige Annahmen trifft, diese Optimierungen aufweist und diese Annahmen überprüft und einen Fallback-Code für den Fall hat, dass diese fehlschlagen. Das betrifft die "Hotspot-Optimierung" und die "Just-in-Time-Kompilierung" - andere Python-Implementierungen wie PyPy tun dies bereits. TL; DR: Ich würde nicht erwarten, dass nicht verwendete Argumente optimiert werden, aber ich denke auch, dass der Overhead völlig vernachlässigbar ist.
mtraceur
51

Der Unterstrich wird für Dinge verwendet, die uns nicht interessieren, und die * in * -Argumente bezeichnen eine Liste von Argumenten. Daher können wir * _ verwenden, um eine Liste von Dingen zu kennzeichnen, die uns nicht interessieren:

def foo(bar, *_):
    return bar

Es besteht sogar die Schecks von PyCharm.

Paul Brown
quelle
Das ist in Python 3.
Andreas verabscheut die Zensur
5
Ich verwende _namelike _rubbishfür Wegwerfparameter aus drei Gründen: a) Pycharm zeigt die Warnung nicht mehr an. b) Ich erinnere mich, was ich wegwerfe. c) Es besteht kein Konflikt mit einem einzelnen Unterstrich.
Thinkeye
_name wird verwendet, um halbprivate Variablen / Klassenattribute zu bezeichnen. Die Verwendung hier ist nicht sinnvoll, da das Ziel darin besteht, die Eingabe zu ignorieren. Es macht jedoch so wenig Sinn, dass es unwahrscheinlich ist, dass es mit der herkömmlichen Verwendung von _name verwechselt wird.
Paul Brown
1
Dies ist eine Standardeinstellung, die von Pylint verwendet wird. Argumente, die mit dem ignored-argument-names=_.*Muster übereinstimmen , dürfen nicht verwendet werden
Anentropic
Die beiden Konventionen kollidieren nicht viel: Bei externen Namen, dh Klassen-, Funktions- und Methodennamen, bedeutet führender Unterstrich "Nicht berühren, es sei denn, Sie wissen, was Sie tun"; für lokale Variablen bedeutet führender Unterstrich "nicht verwendet". Bei Argumenten kollidieren sie jedoch. Argumente werden lokale Variablen BUT in Python sie sind Teil einer externen Schnittstelle der Funktion , wenn der Anrufer sie mit Namen geht. In diesem Fall steht es Ihnen nicht frei, sie umzubenennen. Schlimmer noch, _argist Konvention auch bedeuten , die verwendet „optional private arg, nicht passieren , wenn Sie wissen , was du tust“.
Beni Cherniavsky-Paskin
35

Sie können '_' als Präfix verwenden, damit pylint diese Parameter ignoriert:

def f(a, _b, _c):
Ronyis
quelle
1

Wenn Sie sowohl Argumente als auch Schlüsselwörter haben, sollten Sie diese verwenden

def f(a, *args, **kwargs):
    return a
Maksym Polshcha
quelle
Ich denke, es sollte eine Konfigurationseinstellung in Pylint dafür geben. Es ist üblich, Catch-All- *args, **kwargsFunktionen zu akzeptieren, bei denen Sie sie möglicherweise nicht verwenden oder sich nicht darum kümmern
Anentropic
1
Es ist ein schrecklicher Vorschlag, alle im Universum möglichen Argumente auf den Boden zu werfen.
Boxed
0

Ich denke, die akzeptierte Antwort ist schlecht, aber es kann immer noch funktionieren, wenn Sie das verwenden, was ich als "Perl-Methode" für den Umgang mit Argumenten bezeichnen sollte (ich kenne Perl nicht wirklich, aber ich höre auf, es zu lernen, nachdem ich die subSyntax gesehen habe). mit manuellem Argument entpacken):

Ihre Funktion hat 3 Argumente - so wird sie aufgerufen (Duck tippen, erinnerst du dich?). So bekommen Sie:

def funfun(a, b, c):
    return b * 2

2 nicht verwendete Parameter. Aber jetzt geben Sie einen verbesserten Larsmans-Ansatz ein:

def funfun(*args):
    return args[1] * 2

Und da gehen die Warnungen ...

Trotzdem genieße ich den Weg der Box immer noch mehr:

def funfun(a, b, c):
    del a, c
    return b * 2

Die selbstdokumentierende Qualität der Parameternamen bleibt erhalten. Sie sind eine Funktion, kein Fehler.

Aber die Sprache selbst zwingt Sie nicht dorthin - Sie könnten auch umgekehrt vorgehen und einfach alle Ihre Funktionen mit der Signatur versehen (*args, **kwargs)und das Argument jedes Mal manuell analysieren. Stellen Sie sich vor, wie viel Kontrolle Sie haben. Und keine Ausnahmen mehr, wenn Sie nach dem Ändern Ihrer "Signatur" (Anzahl und Bedeutung der Argumente) veraltet aufgerufen werden. Das ist eine Überlegung wert;)

Tomasz Gandor
quelle
1
Ich denke, die Tatsache, dass Sie einen # unusedKommentar geschrieben haben, ist ein Hinweis darauf, dass es nicht so selbstdokumentierend oder idiomatisch ist, ihn so zu verwenden del.
Frerich Raabe
OK, ich habe den Kommentar entfernt. Jetzt ist es tatsächlich selbstdokumentierend. (Und im Ernst: Sie argumentieren, dass das "Ignorieren" nicht selbstdokumentierend ist. Es ist mir egal, ob das Ignorieren selbstdokumentierend ist, aber ob die API selbstdokumentierend ist).
Tomasz Gandor
1
Dies macht die API nicht selbstdokumentierend. Darüber hinaus ist das, was getan wurde, getan und kann nicht rückgängig gemacht werden: Die Tatsache, dass Sie der Meinung waren, dass es notwendig war, überhaupt einen Kommentar zu schreiben, legt nahe, dass nicht klar ist, was das deltut. Oder denken Sie, dass dunkler Code weniger dunkel wird, wenn Sie Kommentare daraus entfernen?
Jonathan Cast
Sie verstehen nicht, was "selbstdokumentierende API" in diesem Fall bedeutet: Es bedeutet die Tatsache, dass Argumente: 1) vorhanden sind, deren Anzahl bekannt ist; 2) bedeutungsvolle Namen haben. API bedeutet "Schnittstelle", Funktionssignatur. Was unter dem def(..)Strich liegt - Kommentare oder nicht - ist nicht relevant.
Tomasz Gandor
0

Um "unbenutzte Variablen" -Inspektionsnachrichten für nicht verwendete * Argumente und / oder ** kwargs zu vermeiden, ersetze ich argsund kwargsdurch _und __:

def f(a, b, *_, **__):
    ...

Neben dem Entfernen von Nachrichten wird deutlich, dass Sie sich nicht für diese Argumente interessieren.

Ich kann nicht sagen, ob es eine wirklich universelle Lösung ist, aber sie hat überall funktioniert, wo ich sie bisher verwendet habe.

Eric PASCUAL
quelle