Wie sieht die idiomatische Vererbung in Python aus, da Python eine Mehrfachvererbung zulässt?
In Sprachen mit einfacher Vererbung wie Java wird die Vererbung verwendet, wenn Sie sagen können, dass ein Objekt ein anderes Objekt "ist" und Sie Code zwischen den Objekten teilen möchten (vom übergeordneten Objekt zum untergeordneten Objekt). Zum Beispiel könnte man sagen, dass dies Dog
ein Animal
:
public class Animal {...}
public class Dog extends Animal {...}
Da Python jedoch die Mehrfachvererbung unterstützt, können wir ein Objekt erstellen, indem wir viele andere Objekte zusammensetzen. Betrachten Sie das folgende Beispiel:
class UserService(object):
def validate_credentials(self, username, password):
# validate the user credentials are correct
pass
class LoggingService(object):
def log_error(self, error):
# log an error
pass
class User(UserService, LoggingService):
def __init__(self, username, password):
self.username = username
self.password = password
def authenticate(self):
if not super().validate_credentials(self.username, self.password):
super().log_error('Invalid credentials supplied')
return False
return True
Ist dies eine akzeptable oder gute Verwendung der Mehrfachvererbung in Python? Anstatt zu sagen, dass Vererbung ist, wenn ein Objekt ein anderes Objekt "ist", erstellen wir ein User
Modell, das aus UserService
und besteht LoggingService
.
Die gesamte Logik für Datenbank- oder Netzwerkoperationen kann vom User
Modell getrennt gehalten werden, indem sie in das UserService
Objekt eingefügt werden, und die gesamte Logik für die Anmeldung im wird beibehalten LoggingService
.
Ich sehe einige Probleme mit diesem Ansatz:
- Erschafft dies ein Gottobjekt? Da
User
erbt von oder besteht ausUserService
undLoggingService
folgt es wirklich dem Prinzip der Einzelverantwortung? - Um auf Methoden für ein übergeordnetes / next-in-line-Objekt zugreifen zu können (z. B. müssen
UserService.validate_credentials
wir verwendensuper
. Dies macht es etwas schwieriger zu erkennen, welches Objekt mit dieser Methode umgehen wird, und ist nicht so klar wie beispielsweise , instanziierenUserService
und so etwas tunself.user_service.validate_credentials
Was wäre der pythonische Weg, um den obigen Code zu implementieren?
User
Objekte in ihren Schnittstellen nicht nur die in derUser
Klasse definierten Elemente, sondern auch die inUserService
und definiertenLoggingService
. Dies ist keine "has-a" -Beziehung, da die öffentliche Schnittstelle kopiert wird (allerdings nicht durch direktes Kopieren, sondern durch indirektes Nachschlagen der Schnittstellen der Oberklassen).Abgesehen von der Tatsache, dass mehrere Oberklassen zulässig sind, unterscheidet sich die Vererbung von Python nicht wesentlich von der von Java, dh Mitglieder einer Unterklasse sind auch Mitglieder jedes ihrer Supertypen [1]. Die Tatsache, dass Python die Ententypisierung verwendet, spielt ebenfalls keine Rolle: Ihre Unterklasse verfügt über alle Mitglieder ihrer Oberklassen und kann daher von jedem Code verwendet werden, der diese Unterklassen verwenden könnte. Die Tatsache, dass Mehrfachvererbung mithilfe von Komposition effektiv implementiert wird, ist ein roter Faden: Das automatisierte Kopieren der Eigenschaften einer Klasse in eine andere ist das Problem, und es spielt keine Rolle, ob Komposition verwendet wird oder nur auf magische Weise erraten wird, wie die Mitglieder angenommen werden zu arbeiten: sie zu haben ist falsch.
Ja, dies verstößt gegen die Einzelverantwortung, da Sie Ihren Objekten die Möglichkeit geben, Aktionen auszuführen, die nicht logisch Teil ihrer Aufgaben sind. Ja, es werden "Gott" -Objekte geschaffen, was im Wesentlichen eine andere Art ist, dasselbe zu sagen.
Beim Entwerfen objektorientierter Systeme in Python gilt dieselbe Maxime, die in Java-Entwurfsbüchern gepredigt wird: Komposition gegenüber Vererbung bevorzugen. Gleiches gilt für (die meisten [2]) anderen Systeme mit Mehrfachvererbung.
[1]: Man könnte das eine "Ist-Eine" -Beziehung nennen, obwohl ich den Begriff persönlich nicht mag, weil er die Idee der Modellierung der realen Welt nahe legt und die objektorientierte Modellierung nicht mit der realen Welt identisch ist.
[2]: Bei C ++ bin ich mir nicht so sicher. C ++ unterstützt "private Vererbung", bei der es sich im Wesentlichen um eine Komposition handelt, ohne dass ein Feldname angegeben werden muss, wenn Sie die öffentlichen Mitglieder der geerbten Klasse verwenden möchten. Die öffentliche Schnittstelle der Klasse ist davon überhaupt nicht betroffen. Ich benutze es nicht gern , aber ich sehe keinen guten Grund, es nicht zu tun .
quelle