Was ist der pythonische Weg, um Argumente zwischen Funktionen zu übergeben?

8

Ich habe einige Argumente vom Benutzer genommen und Funktion an Funktion übergeben (jede Funktion in einer anderen Klasse), bis es schließlich zu einer Funktion kommt, die etwas verarbeitet und dann die Lösung in der Kette zurückgegeben wird. In der Kette werden die Funktionen immer abstrakter und verschmelzen die Ergebnisse aus mehreren Läufen der unteren Funktionen.

Wo soll ich *args und **kwargs ?

verwenden?

Ich denke, dass *args und *kwargs für jede Funktion verwendet werden können, bei der die Funktion die Argumente nicht explizit verwendet. Die tatsächlichen Argumente müssen jedoch in top_level definiert werden, damit der Benutzer weiß, was die Funktion erwartet.

Wo soll ich definieren, was die Eingänge bedeuten?

Ich denke, dass sie in top_level definiert werden sollten, weil dies der Zweck ist, für den der Endbenutzer die Dokumentation sehen möchte.

Wo soll ich welche Standardwerte definieren?

Noch einmal, ich denke, sie sollten in top_level definiert werden, weil das der ist, mit dem der Endbenutzer interagiert.

Dies ist ein einfaches Beispiel, um die Weitergabe der Argumente zu demonstrieren, wobei ich nicht gezeigt habe, wie die Funktionen immer abstrakter werden oder wie sie mit verschiedenen Klassen interagieren, da ich das Gefühl hatte, dass es unnötig ist.

%Vor%

Gibt es eine Python-Konvention, um solche Argumente zu übergeben?

    
bluprince13 24.03.2017, 15:26
quelle

4 Antworten

2

Dies ist nicht das gebräuchlichste Muster, aber ich habe es für Befehlszeilenprogramme gesehen, die Ebenen von verschachtelten Befehlen haben: Unterbefehle, Unterunterbefehle und so weiter. Das ist ein Modell, bei dem Funktionen der "oberen" Ebene mehr oder weniger Dispatcher sind und keine Informationen darüber haben, welche Parameter von den Unterfunktionen innerhalb einer bestimmten Route benötigt werden. Das reinste Szenario für dieses Modell ist, wenn die Unterbefehle Plugins sind und die "oberen" Schichten buchstäblich keine Informationen über die Unterfunktionen haben, abgesehen von einer Aufrufkonvention, an die sich die Plugins halten sollen.

In diesen Fällen würde ich argumentieren, dass der pythonische Weg darin besteht, Parameter von höheren zu niedrigeren Funktionen zu übergeben und die Arbeitsebene zu entscheiden, welche nützlich ist. Der Bereich der möglichen Parameter würde in der Aufrufkonvention definiert werden. Dies ist pythisch auf der Basis von DRY - wiederhole dich nicht. Wenn die Low-Level / Worker-Funktion definiert, welche Eingaben erforderlich oder optional sind, ist es oft sinnvoll, diese Informationen nicht auf den höheren Ebenen zu wiederholen.

Dasselbe gilt für ein Inversion-of-Control-Flow-Design, nicht nur für CLI-Anwendungen mit Plug-ins. Es gibt viele Anwendungsdesigns, bei denen ich diesen Ansatz nicht verwenden würde, aber es funktioniert hier.

Die Bedeutung eines Eingangs muss auf der obersten Ebene festgelegt werden, in der er entstehen kann - als Interface-Spezifikation für niedrigere Ebenen (eine Konvention, nicht programmatisch). Andernfalls hätten die Eingaben keine semantische Bedeutung.

Wenn eine Eingabe von mehreren Unterfunktionen verwendet werden kann, d. h. es liegt ein Verkettungs- oder Pipeline-Konzept im Kontrollfluss vor, muss der Standardwert einer Eingabe auch auf der obersten Ebene für die Eingabe definiert werden.

    
Chris Johnson 02.04.2017, 17:59
quelle
6

Ich werde Ihre Frage nicht beantworten, weil es so wäre, als würde ich die Frage beantworten: "Was ist der beste Weg, einen Schraubenzieher zu verwenden, um eine Nuss festzuziehen?". I.e. Ich glaube nicht, dass die Tools, mit denen Sie nach Anleitungen suchen ( *args und **kwargs ), dazu dienen, das Problem zu lösen, das Sie lösen möchten.

Stattdessen beantworte ich diese Frage: "Wie verknüpfe ich einige Daten mit einer Reihe von Funktionen?", und die Antwort darauf ist eindeutig Klassen verwenden .

Willkommen bei der objektorientierten Programmierung. Ich denke du wirst es genießen!

Dies ist ein sehr einfaches Beispiel dessen, was ich meine, aber es war schwer genau zu wissen, was Sie von Ihrem Beispiel wollten, da es einfach war, aber das Grundprinzip besteht darin, Ihre Daten in einer Klasse zu kapseln und dann mit ihr zu arbeiten die Methoden der Klasse.

  • Sie können dann zwischen den Methoden in der Klasse aufrufen, ohne dass Sie ständig viele Argumente übergeben müssen (wie zum Beispiel die Methode .calculate() ), von denen Sie nicht wissen, ob die oberste Ebene benötigt wird oder eine unterste Ebene .
  • Sie können die Parameter nur an einer Stelle dokumentieren, die Methode __init__ .
  • Sie können durch Unterklassen transparent für den Code anpassen (denn wenn Sie eine Methode in einer Unterklasse überschreiben, kann sie immer noch von der übergeordneten Oberklasse verwendet werden), wie ich es für die .reduce(x, y) -Methode unten getan habe.

Beispiel:

%Vor%     
daphtdazz 03.04.2017 15:59
quelle
3

Wie wäre es mit diesem Ansatz: Erstellen Sie eine Klasse, nennen Sie sie AllInputs , die die Sammlung aller "Argumente des Benutzers" darstellt. Der einzige Zweck dieser Klasse besteht darin, als Container für eine Gruppe von Werten zu dienen. Eine Instanz dieser Klasse wird natürlich auf der obersten Ebene des Programms initialisiert.

%Vor%

Dieses Objekt, nennen wir es all_inputs , wird nun als einziges Argument an alle Funktionen in Ihrem Beispiel übergeben. Wenn eine Funktion keines der Felder im Objekt verwendet, ist das in Ordnung; es geht einfach an die untergeordnete Funktion weiter, wo die eigentliche Arbeit erledigt wird. Um Ihr Beispiel zu überarbeiten, müssten Sie jetzt:

%Vor%

Ich weiß nicht, ob das "Pythonic" oder "Nicht-Pythonic" ist und es mir egal ist. Ich denke, es ist eine gute Programmieridee, die Daten, die das Programm verwendet, zusammenzufassen. Der Initialisierungsprozess, bei dem Standardwerte mit anderen vom Benutzer übernommenen Werten kombiniert werden, ist an einer Stelle zentralisiert, an der er leicht zu verstehen ist. Es ist einigermaßen selbstdokumentierend. Sie sagen, die Funktionsaufrufe sind auf mehrere Klassen verteilt, und das ist kein Problem. Die Funktionsaufrufe sind sauber und der Programmablauf ist einfach zu folgen. Es gibt Optimierungspotential, indem Sie einen Teil der Berechnung in AllInputs platzieren, damit Sie den Duplizierungscode vermeiden können.

Was ich in Ihrem Beispiel nicht mag (und ich denke, dass Sie es auch nicht mögen, oder Sie hätten die Frage wahrscheinlich gar nicht erst gestellt), ist, wie es die Syntax *args verwendet. Wenn ich diese Syntax sehe, nehme ich an, dass alle Argumente dieselbe semantische Bedeutung haben, wie in der Standardbibliotheksfunktion os.path.join . Wenn ich die Frage in Ihrer Anwendung verstehe, müssen die Low-Level-Funktionen die Argumentliste in einer bestimmten Reihenfolge und mit bestimmten Bedeutungen haben (Ihr Beispiel gibt das nicht wieder, aber der Text schlägt es vor). Es ist verwirrend zu sehen, dass Argumente in einer Funktion als *args übergeben werden und dann auf einer niedrigeren Ebene ihre spezifischen Namen und Bedeutungen wieder erscheinen. Wenn Sie sie zu einem einzigen Objekt gruppieren, wird deutlich, was vor sich geht.

    
Paul Cornelius 08.04.2017 18:33
quelle
1

Ich würde argumentieren, dass das Übergeben von Argumenten über mehrere Funktionsebenen nicht pythisch ist.

Aus dem Zen von Python:

  • Einfach ist besser als komplex
  • Flat ist besser als verschachtelt

Bearbeiten :

Wenn es viele Argumente gibt und die Funktionen dazwischen sie einfach weitergeben, würde ich sie wahrscheinlich in ein tuple einpacken und sie auf der niedrigsten Ebene auspacken.

    
Roland Smith 02.04.2017 10:10
quelle

Tags und Links