Was ich beim Nachahmen mache, ist das Einbetten in Python oder eher die Delegierung als die Vererbung.
Einer der Gründe, warum Vererbung schmerzhaft ist, ist, dass Sie nicht wissen, welche Methode aufgerufen wurde.
Zum Beispiel für C mit der folgenden Vererbungsbeziehung
class A:
def f(self):
return "f"
def g(self):
return "g"
class B:
def h(self):
return "h"
def i(self):
return "i"
class C(A, B):
def m(self):
return (self.f(), self.i())
Es ist schwierig, das Verarbeitungsverhalten von "C # m ()" zu verstehen. Natürlich denke ich, dass ich normalerweise etwas genauer über den Namen bin, also denke ich nicht, dass es so schlimm sein sollte.
Auf der anderen Seite ist es immer noch besser, wenn es delegiert wird.
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
Dies liegt daran, dass angegeben wird, welche Methode der intern gehaltenen Instanzvariablen aufgerufen wird.
Hier ist die Einbettung von go. Dies kann auch als eine Funktion angesehen werden, die die Delegierung halbautomatisch ausführt.
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
ReadeWriter verfügt über Reader- und Writer-Funktionen. Im Wesentlichen delegiert es nur an den Wert, den es in sich hat. Das obige Beispiel war ein Beispiel für eine Schnittstelle, aber das Gleiche gilt für struct.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Wenn Sie so etwas wie "rw.Read ()" für eine Variable rw ausführen, die hier zu "var rw * ReadWriter" wird, wird so etwas wie "rw.Reader.Read ()" aufgerufen.
Geh zurück zu Python. Sie haben im ersten Beispiel erwähnt, dass Delegierung besser ist als Vererbung. Es wurde jedoch von der Delegation nicht vollständig gelöst. Möglicherweise möchten Sie die Methoden A und B direkt von C aus aufrufen. Wenn Sie etwas wie "c.a.f ()" direkt dort ausführen, wo Sie C verwenden, werden die Werte, die tatsächlich verwendet werden können, auf die C-Instanz festgelegt.
Aus diesem Grund möchten Sie, dass "c.a.f ()" intern aufgerufen wird, während Sie "c.f ()" aufrufen. Es ist jedoch schmerzhaft, die erforderlichen Methodenaufrufe jedes Mal explizit zu schreiben. Ich würde mich freuen, wenn Sie es automatisch tun könnten. Mit anderen Worten, es ist eine Nachahmung der Einbettung go.
Das Hauptthema ist von hier aus, aber wenn Sie die Einbettung von go with python imitieren möchten, mache ich persönlich Folgendes.
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
def __getattr__(self, name):
return getattr(self.a, name)
Dies ändert automatisch "c.f" in "c.a.f". Dies ahmt die Einbettung von go nicht vollständig nach. Wenn ein nicht vorhandenes Attribut angegeben wird, wird alles an self.a delegiert. Sie können dies etwas expliziter oder restriktiver tun, indem Sie Metaprogrammierung usw. verwenden. Ich denke das ist genug für jetzt.
Im ursprünglichen Beispiel gab es mehrere Vererbungsquellen. Im vorherigen Beispiel können wir nur den Fall behandeln, in dem es nur eine Vererbungsquelle gibt. Schauen wir uns auch mehrere Fälle an. Der zu beachtende Punkt ist, wie man getattr verwendet. Sie können den Standardwert für das dritte Argument von getattr angeben. Versuchen Sie, anstelle von None einen speziellen Wert zurückzugeben. Dies liegt daran, dass selbst wenn Keine festgelegt ist, nach dem nächsten Suchziel gesucht wird (im Gegenteil, wenn Sie nach einem anderen Kandidaten suchen möchten, wenn Keine vorhanden ist, können Sie dies tun. Verstehen Sie das Verhalten. Ich empfehle es nicht, weil es schwierig sein wird).
missing = object()
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
def __getattr__(self, name):
v = getattr(self.a, name, missing)
if v is not missing:
return v
return getattr(self.b, name)
Jetzt ruft c.i ()
c.b.i () auf und
c.f () ruft
c.a.f () `auf.
Übrigens habe ich im ursprünglichen Beispiel ein Beispiel geschrieben, in dem Werte, die mehrere A und B erben, im letzten Beispiel in mehreren Übertragungszielen unterstützt werden. Im tatsächlichen Code wird nicht empfohlen, eine solche implizite Übertragung an mehrere Übertragungsziele durchzuführen. Dies liegt daran, dass es schwierig wird, das Verhalten durch Betrachten des Putt zu erfassen. Daher ist es besser, das Übertragungsziel so weit wie möglich auf nur ein Ziel zu beschränken.
Recommended Posts