Grundlegende Methodenverkettung

Lesezeit: 4 Minuten

Grundlegende Methodenverkettung
Pezze

Ich habe diese Methodenverkettung in Python gefunden, aber selbst damit konnte ich die Methodenverkettung in Python nicht verstehen.

Hier sind die Ziele zwei: das Codierungsproblem lösen und die Methodenverkettung verstehen (da ich mit Callables immer noch nicht 100% sicher bin).

Bis zur Problemstellung.

Ich möchte eine Klasse mit zwei Methoden: eine setzt einen Parameter des Objekts = “line” und die andere überschreibt ‘bar’.

Das habe ich bisher bekommen:

class foo():
    def __init__(self, kind=None):
        self.kind = kind

    def __call__(self, kind=None):
        return foo(kind=kind)

    def my_print(self):
        print (self.kind)

    def line(self):
        return self(kind='line')
    def bar(self):
        return self(kind='bar')

Mit diesem Code kann ich mein Ziel leider erreichen

a = foo()
a.bar().line().bar().bar().line().my_print()

Aber ich möchte das gleiche Ergebnis erzielen, indem ich diesen Code schreibe

a = foo()
a.bar.line.bar.bar.line.my_print()

Wie erreiche ich das? Ich schätze, etwas stimmt nicht damit, wie ich das definiert habe __call__ Methode. Vielen Dank im Voraus für Ihre Hilfe.

Grundlegende Methodenverkettung
Robᵩ

Methodenverkettung ist einfach hinzufügen zu können .second_func() zu was auch immer .first_func() kehrt zurück. Es ist ziemlich einfach zu implementieren, indem sichergestellt wird, dass alle verkettebaren Methoden zurückgeben self. (Beachte, dass dies nichts zu tun hat mit __call()__).

class foo():
    def __init__(self, kind=None):
        self.kind = kind
    def my_print(self):
        print (self.kind)
        return self
    def line(self):
        self.kind = 'line'
        return self
    def bar(self):
        self.kind='bar'
        return self

Sie können verwenden foo Objekte auf nicht verkettete Weise, indem ihre zurückgegebenen Werte ignoriert werden:

a = foo()
a.line()
a.my_print()
a.bar()
a.my_print()

assert a.kind == 'bar'

Da jede Funktion jetzt das Objekt selbst zurückgibt, können Sie direkt mit dem zurückgegebenen Wert arbeiten. Sie können die Methodenverkettung mit diesem entsprechenden Code verwenden:

b = foo()
b.line().my_print().bar().my_print()
assert b.kind == 'bar'

Oder auch:

c = foo().line().my_print().bar().my_print()
assert c.kind == 'bar'

Die Frage, das loszuwerden () Aufrufsyntax ist a völlig separates Konzept aus Methodenketten. Wenn Sie Ketteneigenschaften wünschen und diese Eigenschaften ihr Objekt mutieren lassen, verwenden Sie die @property Dekorateur. (Aber das Mutieren von Objekten über eine Eigenschaft scheint gefährlich. Verwenden Sie besser eine Methode und benennen Sie sie mit einem Verb: .set_line() anstatt .line, zum Beispiel.)

class foo():
    def __init__(self, kind=None):
        self.kind = kind
    def my_print(self):
        print (self.kind)
        return self
    @property
    def line(self):
        self.kind = 'line'
        return self
    @property
    def bar(self):
        self.kind='bar'
        return self

a = foo()
a.line
a.my_print()
a.bar
a.my_print()

assert a.kind == 'bar'

b = foo()
b.line.my_print().bar.my_print()
assert b.kind == 'bar'

c = foo().line.my_print().bar.my_print()
assert c.kind == 'bar'

  • Danke für die sehr ausführliche Antwort!!

    – Pezze

    24. Januar ’17 um 0:16

  • @Rob Danke! wie es immer wiederkehrt self , wie können wir Daten an der letzten Kette zurückgeben. in diesem Fall return .kind

    – Akhilesh_IN

    24. Juni ’19 um 7:53

  • @Akhilesh Vielleicht so: b.line.my_print().bar.my_print().kind.

    – Robᵩ

    24. Juni ’19 um 19:55

Verwenden Sie Eigenschaften (Deskriptoren).

class foo:
    def __init__(self, kind=None):
        self.kind = kind

    def __call__(self, kind=None):
        return foo(kind=kind)

    def my_print(self):
        print (self.kind)

    @property
    def line(self):
        return self(kind='line')

    @property
    def bar(self):
        return self(kind='bar')

Beachten Sie jedoch, dass Sie nichts überschreiben, die Änderung funktioniert nicht direkt (was übrigens gut ist). Wie auch immer, dies scheint für die meisten realen Fälle keine gute Designwahl zu sein, da Ihre Methoden irgendwann Argumente erfordern.

Es gibt noch einen anderen interessanten Weg, dies zu erreichen

class Foo:
    def __init__(self, kind=[]):
        self.kind = kind

    def __getattr__(self, attrs):
        self.attrs = attrs
        return Foo(self.kind + [attrs]) 

    def __call__(self):
        return self.kind[::-1][0]


my_obj = Foo()
print(my_obj.bar.line.bar.bar.line())

Mit diesem Code musst du nicht bestehen .my_print() Aber eine Sache, die hier zu beachten ist, ist, dass die Foo-Klasse alles als Argument nimmt, wie wenn wir es versuchen print(my_obj.bar.line.bar.bar.circle()) es wird Kreis zurückkehren.

Sie können diesen Code auch bearbeiten, um die Argumente beim Aufrufen einer beliebigen Funktion zu verwenden.

.

289770cookie-checkGrundlegende Methodenverkettung

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

Privacy policy