Zweck von "return self" aus einer Klassenmethode?

46

In einem Open-Source-Projekt bin ich auf so etwas gestoßen. Methoden, die Instanzattribute ändern, geben einen Verweis auf die Instanz zurück. Was ist der Zweck dieses Konstrukts?

class Foo(object):

  def __init__(self):
    self.myattr = 0

  def bar(self):
    self.myattr += 1
    return self
nate c
quelle
2
Und so ist so ziemlich alles in jQuery geschrieben. Nahezu jede Funktion gibt ein jQuery-Objekt zurück
CaffGeek 23.12.10
11
Warum würden Sie nicht so geschriebenem Code vertrauen?
Sonntag,

Antworten:

65

Es soll das Verketten erlauben.

Zum Beispiel:

var Car = function () {
    return {
        gas : 0,
        miles : 0,
        drive : function (d) {
            this.miles += d;
            this.gas -= d;
            return this;
        },
        fill : function (g) {
            this.gas += g;
            return this;
        },
    };
}

Nun können Sie sagen:

var c = Car();
c.fill(100).drive(50);
c.miles => 50;
c.gas => 50;
Josh K
quelle
20
Für das Protokoll wird diese Art der Verkettung im Allgemeinen in Code mit flüssiger Benutzeroberfläche verwendet .
Lie Ryan
10

Wie @Lie Ryan und @Frank Shearar erwähnen, nennt man das eine "fließende Schnittstelle", aber dieses Muster gibt es schon sehr lange.

Der umstrittene Teil dieses Musters ist, dass Sie in OO einen veränderlichen Zustand haben. Eine Void-Methode hat also eine Art impliziten Rückgabewert von this- das Objekt mit dem aktualisierten Zustand ist also eine Art Rückgabewert.

In einer OO-Sprache mit veränderlichem Zustand sind diese beiden also mehr oder weniger gleichwertig:

a.doA()
a.doB()
a.doC()

...im Gegensatz zu

a.doA().doB().doC()

Daher habe ich in der Vergangenheit gehört, dass Menschen sich fließenden Schnittstellen widersetzen, weil sie die erste Form mögen. Ein anderer Name, den ich für "fließendes Interface" gehört habe, ist "Zugunglück";)

Ich sage jedoch "mehr oder weniger gleichwertig", weil fließende Schnittstellen eine Falte hinzufügen. Sie müssen dies nicht "zurückgeben". Sie können "neu zurückkehren". Dies ist eine Möglichkeit, in OO unveränderliche Objekte zu erhalten.

Sie könnten also eine Klasse A haben, die dies tut (Pseudocode)

function doA():
    return new A(value + 1)

function doB():
    return new A(value * 2)

function doC():
    return new A(sqrt(value))

Jetzt gibt jede Methode ein brandneues Objekt zurück, wobei das ursprüngliche Objekt unverändert bleibt. Und das ist eine Möglichkeit, in unveränderliche Objekte zu gelangen, ohne wirklich viel an Ihrem Code zu ändern.

rauben
quelle
Ok, aber wenn Ihre Methoden zurückkehren new(...), würde das Speicher verlieren.
SMCI
@smci Das würde stark von der Sprache, Implementierung und Verwendung abhängen. Sicher, wenn es C ++ ist, werden Sie wahrscheinlich überall Speicher verlieren. Aber dies alles würde trivialerweise durch eine Sprache mit einem Müllsammler aufgeräumt. Einige Implementierungen (mit oder ohne GC) erkennen möglicherweise sogar, wenn eines dieser neuen Objekte nicht verwendet wird und weisen nichts zu.
8bittree
@ 8bittree: Wir sprechen über Python, diese Frage wurde vor 8 Jahren mit Python getaggt. Python GC ist nicht so toll, dass es das Beste ist, überhaupt keinen Speicher zu verlieren. Durch __init__wiederholtes Aufrufen wird auch der Overhead erhöht.
SMCI
8

Die meisten Sprachen kennen das "return self" -Idiom und ignorieren es, wenn es nicht in einer Zeile verwendet wird. Es ist jedoch bemerkenswert, dass in Python Funktionen Nonestandardmäßig zurückgegeben werden.

Als ich in der CS-Schule war, machte mein Ausbilder eine große Sache über den Unterschied zwischen Funktionen, Prozeduren, Routinen und Methoden. Viele handverkrampfte Essayfragen wurden mit Druckbleistiften herausgeschliffen, die mir bei all dem heiß wurden.

Es genügt zu sagen, dass die Rückkehr zu sich selbst der endgültige Weg ist, Klassenmethoden zu erstellen, aber Python erlaubt mehrere Rückgabewerte, Tupel, Listen, Objekte, Primitive oder Keine.

Verkettung bedeutet lediglich, die Antwort auf die letzte Operation in die nächste zu verschieben, und die Laufzeit von Python kann so etwas optimieren. Listenverständnisse sind eine eingebaute Form davon. (Sehr kraftvoll!)

Daher ist es in Python nicht so wichtig, dass jede Methode oder Funktion Dinge zurückgibt, weshalb der Standardwert None ist.

Es besteht die Meinung, dass jede Aktion in einem Programm ihren Erfolg, ihr Scheitern oder ihr Ergebnis an den aufrufenden Kontext oder das aufrufende Objekt zurückmelden sollte, aber hier nicht über die DOD-ADA-Anforderungen gesprochen hat. Wenn Sie Feedback von einer Methode benötigen, gehen Sie vor oder nicht, aber versuchen Sie, diesbezüglich konsistent zu sein.

Wenn eine Methode fehlschlagen kann, sollte sie Erfolg oder Misserfolg zurückgeben oder eine zu behandelnde Ausnahme auslösen.

Eine Einschränkung ist, dass Sie mit Python alle Ihre Methoden Variablen zuweisen können, wenn Sie die Rückgabeselbstbezeichnung verwenden, und Sie möglicherweise glauben, dass Sie ein Datenergebnis oder eine Liste erhalten, wenn Sie das Objekt tatsächlich erhalten.

Typbeschränkende Sprachen schreien und schreien und brechen, wenn Sie dies versuchen, aber interpretierte Sprachen (Python, Lua, Lisp) sind viel dynamischer.

Chris Reid
quelle
4

In Smalltalk hat jede Methode, die nicht explizit etwas zurückgibt, ein implizites "return self".

Dies liegt daran, dass (a) jede Methode etwas zurückgibt, was das Rechenmodell einheitlicher macht, und (b) es sehr nützlich ist, wenn Methoden sich selbst zurückgeben. Josh K gibt ein schönes Beispiel.

Frank Shearar
quelle
Interessant ... in Python, jede Methode, die nicht explizit etwas zurückgibt, hat eine implizite "return None".
Job
2

Vorteile der Rückgabe eines Objekts ( return self)

  • Beispiel:

    print(foo.modify1().modify2())
    
    # instaed of
    foo.modify1()
    foo.modify2()
    print(foo)
    
  • Populärer Stil in der Programmiergemeinschaft (glaube ich).

Vorteile der Mutation eines Objekts (nein return self)

  • Möglichkeit zur Verwendung returnfür andere Zwecke.
  • Guido van Rossum ist mit diesem Stil einverstanden (ich bin mit seiner Argumentation nicht einverstanden).
  • Populärerer Stil in der Python-Community (glaube ich).
  • Standard (weniger Quellcode).
xged
quelle
Das Guido-Argument überzeugt mich. Insbesondere der Teil, in dem er über "den Leser muss mit jeder der Methoden bestens vertraut sein" spricht, d. H., Sie müssen zunächst feststellen, ob dies eine Selbstkette oder eine Ausgangskette oder eine Kombo ist, bevor Sie verstehen, was Sie am Ende von haben chain
Nath
@Nat Inwiefern unterscheiden sich Zeichenfolgenverarbeitungsoperationen grundlegend von den übrigen Operationen?
xged
Der Unterschied besteht darin, dass der String-Fall jedes Mal ein neues, völlig anderes Objekt generiert. Dh das Element wird nicht geändert. Es ist klar, dass das vorhandene Objekt nicht geändert wird. In einer Sprache, in der Return Self implizit ist, ist das Gegenteil der Fall, aber die gemischte Umgebung scheint problematisch
Nath
@Matt Mir ist gerade eingefallen, dass Python-Strings unveränderlich sind. Das beantwortet meine Frage vollständig.
xged
1
@Matt "Es ist klar, dass das vorhandene Objekt nicht geändert wird" - es ist jedoch nicht klar, wenn man nur hinschaut.
xged