Was ist der Sinn der Vererbung in Python?

82

Angenommen, Sie haben die folgende Situation

#include <iostream>

class Animal {
public:
    virtual void speak() = 0;
};

class Dog : public Animal {
    void speak() { std::cout << "woff!" <<std::endl; }
};

class Cat : public Animal {
    void speak() { std::cout << "meow!" <<std::endl; }
};

void makeSpeak(Animal &a) {
    a.speak();
}

int main() {
    Dog d;
    Cat c;
    makeSpeak(d);
    makeSpeak(c);
}

Wie Sie sehen können, ist makeSpeak eine Routine, die ein generisches Animal-Objekt akzeptiert. In diesem Fall ist Animal einer Java-Schnittstelle ziemlich ähnlich, da es nur eine rein virtuelle Methode enthält. makeSpeak kennt die Art des Tieres, an dem es übergeben wird, nicht. Es sendet nur das Signal "speak" und verlässt die späte Bindung, um sich darum zu kümmern, welche Methode aufgerufen werden soll: entweder Cat :: speak () oder Dog :: speak (). Dies bedeutet, dass für makeSpeak die Kenntnis, welche Unterklasse tatsächlich übergeben wird, irrelevant ist.

Aber was ist mit Python? Sehen wir uns den Code für denselben Fall in Python an. Bitte beachten Sie, dass ich für einen Moment versuche, dem C ++ - Fall so ähnlich wie möglich zu sein:

class Animal(object):
    def speak(self):
        raise NotImplementedError()

class Dog(Animal):
    def speak(self):
        print "woff!"

class Cat(Animal):
    def speak(self):
        print "meow"

def makeSpeak(a):
    a.speak()

d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)

In diesem Beispiel sehen Sie dieselbe Strategie. Sie verwenden die Vererbung, um das hierarchische Konzept von Hunden und Katzen als Tiere zu nutzen. In Python ist diese Hierarchie jedoch nicht erforderlich. Das funktioniert genauso gut

class Dog:
    def speak(self):
        print "woff!"

class Cat:
    def speak(self):
        print "meow"

def makeSpeak(a):
    a.speak()

d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)

In Python können Sie das Signal "Sprechen" an jedes gewünschte Objekt senden. Wenn das Objekt damit umgehen kann, wird es ausgeführt, andernfalls wird eine Ausnahme ausgelöst. Angenommen, Sie fügen beiden Codes eine Klasse Flugzeug hinzu und senden ein Flugzeugobjekt an makeSpeak. Im C ++ - Fall wird es nicht kompiliert, da Airplane keine abgeleitete Tierklasse ist. Im Fall von Python wird zur Laufzeit eine Ausnahme ausgelöst, die sogar ein erwartetes Verhalten sein kann.

Angenommen, Sie fügen auf der anderen Seite eine MouthOfTruth-Klasse mit einer Methode speak () hinzu. Im C ++ - Fall müssen Sie entweder Ihre Hierarchie umgestalten oder eine andere makeSpeak-Methode definieren, um MouthOfTruth-Objekte zu akzeptieren, oder Sie können in Java das Verhalten in ein CanSpeakIface extrahieren und die Schnittstelle für jedes implementieren. Es gibt viele Lösungen ...

Ich möchte darauf hinweisen, dass ich noch keinen einzigen Grund gefunden habe, die Vererbung in Python zu verwenden (abgesehen von Frameworks und Ausnahmebäumen, aber ich denke, dass es alternative Strategien gibt). Sie müssen keine von der Basis abgeleitete Hierarchie implementieren, um eine polymorphe Leistung zu erzielen. Wenn Sie die Vererbung verwenden möchten, um die Implementierung wiederzuverwenden, können Sie dies durch Eindämmung und Delegierung erreichen. Der zusätzliche Vorteil besteht darin, dass Sie sie zur Laufzeit ändern und die Schnittstelle des enthaltenen Systems klar definieren können, ohne unbeabsichtigte Nebenwirkungen zu riskieren.

Am Ende stellt sich also die Frage: Was ist der Sinn der Vererbung in Python?

Edit : danke für die sehr interessanten Antworten. Sie können es zwar für die Wiederverwendung von Code verwenden, aber ich bin immer vorsichtig, wenn Sie die Implementierung wiederverwenden. Im Allgemeinen neige ich dazu, sehr flache Vererbungsbäume oder gar keinen Baum zu erstellen, und wenn eine Funktionalität gemeinsam ist, überarbeite ich sie als gemeinsame Modulroutine und rufe sie dann von jedem Objekt aus auf. Ich sehe den Vorteil, einen einzigen Änderungspunkt zu haben (z. B. anstatt Hund, Katze, Elch usw. hinzuzufügen, füge ich nur Tier hinzu, was der grundlegende Vorteil der Vererbung ist), aber Sie können dasselbe mit erreichen eine Delegierungskette (z. B. a la JavaScript). Ich behaupte nicht, dass es besser ist, nur auf eine andere Weise.

Ich habe diesbezüglich auch einen ähnlichen Beitrag gefunden .

Stefano Borini
quelle
18
-1: "Sie können dasselbe mit einer Delegierungskette erreichen". Richtig, aber weitaus schmerzhafter als die Vererbung. Sie können dasselbe erreichen, ohne überhaupt Klassendefinitionen zu verwenden, sondern nur viele komplizierte reine Funktionen. Sie können dasselbe auf ein Dutzend Arten erreichen, alles weniger einfach als Vererbung.
S.Lott
10
in der Tat sagte ich "Ich behaupte nicht, dass es besser ist;)"
Stefano Borini
4
"Ich habe noch keinen einzigen Grund gefunden, die Vererbung in Python zu verwenden" ... klingt sicher wie "meine Lösung ist besser".
S.Lott
9
Entschuldigung, wenn es Ihnen diesen Eindruck vermittelt hat. Mein Beitrag zielte darauf ab, positives Feedback für reale Fallbeispiele zur Verwendung der Vererbung in Python zu erhalten, die ich bis heute nicht finden konnte (hauptsächlich, weil ich bei all meiner Python-Programmierung auf Fälle stieß, in denen dies nicht erforderlich war und wann Ich habe es getan, es war die Situation, die ich oben erklärt habe.
Stefano Borini
2
Reale Taxonomien sind selten eine gute Grundlage für Beispiele der Objektorientierung.
Apalala

Antworten:

81

Sie bezeichnen die Enten-Typisierung zur Laufzeit als "überschreibende" Vererbung. Ich glaube jedoch, dass Vererbung als Entwurfs- und Implementierungsansatz ihre eigenen Vorzüge hat und ein wesentlicher Bestandteil des objektorientierten Entwurfs ist. Meiner bescheidenen Meinung nach ist die Frage, ob Sie etwas anderes erreichen können, nicht sehr relevant, da Sie Python tatsächlich ohne Klassen, Funktionen und mehr codieren könnten, aber die Frage ist, wie gut gestaltet, robust und lesbar Ihr Code sein wird.

Ich kann zwei Beispiele dafür nennen, wo Vererbung meiner Meinung nach der richtige Ansatz ist. Ich bin sicher, dass es noch mehr gibt.

Wenn Sie mit Bedacht codieren, möchte Ihre makeSpeak-Funktion möglicherweise überprüfen, ob es sich bei der Eingabe tatsächlich um ein Tier handelt und nicht nur darum, dass "es sprechen kann". In diesem Fall wäre die eleganteste Methode die Verwendung der Vererbung. Auch hier können Sie dies auf andere Weise tun, aber das ist das Schöne an objektorientiertem Design mit Vererbung - Ihr Code prüft "wirklich", ob die Eingabe ein "Tier" ist.

Zweitens und deutlich einfacher ist die Kapselung - ein weiterer wesentlicher Bestandteil des objektorientierten Designs. Dies wird relevant, wenn der Vorfahr Datenelemente und / oder nicht abstrakte Methoden hat. Nehmen Sie das folgende dumme Beispiel, in dem der Vorfahr eine Funktion (speak_twice) hat, die eine dann abstrakte Funktion aufruft:

class Animal(object):
    def speak(self):
        raise NotImplementedError()

    def speak_twice(self):
        self.speak()
        self.speak()

class Dog(Animal):
    def speak(self):
        print "woff!"

class Cat(Animal):
    def speak(self):
        print "meow"

Vorausgesetzt, es "speak_twice"handelt sich um eine wichtige Funktion, möchten Sie sie nicht sowohl in Dog als auch in Cat codieren, und ich bin sicher, dass Sie dieses Beispiel extrapolieren können. Sicher, Sie könnten eine eigenständige Python-Funktion implementieren, die ein Objekt vom Typ Ente akzeptiert, prüfen, ob es eine Sprechfunktion hat, und es zweimal aufrufen, aber das ist nicht elegant und verfehlt Punkt 1 (überprüfen Sie, ob es sich um ein Tier handelt). Schlimmer noch, und um das Beispiel der Kapselung zu stärken, was wäre, wenn eine Mitgliedsfunktion in der Nachkommenklasse verwenden wollte "speak_twice"?

Es wird noch deutlicher, wenn die Vorfahrenklasse ein Datenelement hat, das beispielsweise "number_of_legs"von nicht abstrakten Methoden im Vorfahren wie verwendet wird "print_number_of_legs", aber im Konstruktor der Nachkommenklasse initiiert wird (z. B. würde Dog es mit 4 initialisieren, während Snake es initialisieren würde mit 0).

Ich bin mir wieder sicher, dass es unendlich viele weitere Beispiele gibt, aber im Grunde erfordert jede (groß genug) Software, die auf einem soliden objektorientierten Design basiert, eine Vererbung.

Roee Adler
quelle
3
Für den ersten Fall würde dies bedeuten, dass Sie Typen anstelle von Verhalten überprüfen, was irgendwie unpythonisch ist. Für den zweiten Fall stimme ich zu, und Sie machen im Grunde den "Framework" -Ansatz. Sie recyceln die Implementierung von speak_twice, nicht nur die Schnittstelle, sondern können beim Überschreiben ohne Vererbung leben, wenn Sie Python in Betracht ziehen.
Stefano Borini
9
Sie können ohne viele Dinge wie Klassen und Funktionen leben, aber die Frage ist, was Code großartig macht. Ich denke, Vererbung tut es.
Roee Adler
@Stefano Borini - Es hört sich so an, als würden Sie einen sehr "regelbasierten" Ansatz verfolgen. Das alte Klischee ist jedoch wahr: Sie wurden gemacht, um gebrochen zu werden. :-)
Jason Baker
@ Jason Baker - Ich mag Regeln, weil sie über Erfahrungen berichten (z. B. Fehler), aber ich mag es nicht, wenn Kreativität durch sie behindert wird. Also stimme ich Ihrer Aussage zu.
Stefano Borini
1
Ich finde dieses Beispiel nicht so klar - Tiere, Autos und Formbeispiele sind wirklich schlecht für diese Diskussionen :) Das einzige, was meiner Meinung nach zählt, ist, ob Sie die Implementierung erben wollen oder nicht. Wenn ja, sind die Regeln in Python Java / C ++ sehr ähnlich. Der Unterschied besteht hauptsächlich in der Vererbung von Schnittstellen. In diesem Fall ist Enten-Typisierung oft die Lösung - viel mehr als Vererbung.
David Cournapeau
12

Bei der Vererbung in Python dreht sich alles um die Wiederverwendung von Code. Faktorisieren Sie allgemeine Funktionen in eine Basisklasse und implementieren Sie unterschiedliche Funktionen in den abgeleiteten Klassen.

Roberto Bonvallet
quelle
11

Die Vererbung in Python ist mehr eine Bequemlichkeit als alles andere. Ich finde, dass es am besten verwendet wird, um eine Klasse mit "Standardverhalten" zu versehen.

In der Tat gibt es eine bedeutende Community von Python-Entwicklern, die sich überhaupt gegen die Verwendung von Vererbung aussprechen. Was auch immer Sie tun, übertreiben Sie es nicht. Eine übermäßig komplizierte Klassenhierarchie ist ein sicherer Weg, um als "Java-Programmierer" bezeichnet zu werden, und das kann man einfach nicht haben. :-)

Jason Baker
quelle
8

Ich denke, der Punkt der Vererbung in Python besteht nicht darin, den Code kompilieren zu lassen, sondern aus dem wahren Grund der Vererbung die Klasse in eine andere untergeordnete Klasse zu erweitern und die Logik in der Basisklasse zu überschreiben. Die Enten-Eingabe in Python macht das "Interface" -Konzept jedoch unbrauchbar, da Sie vor dem Aufruf einfach überprüfen können, ob die Methode vorhanden ist, ohne eine Schnittstelle zum Einschränken der Klassenstruktur verwenden zu müssen.

bashmohandes
quelle
3
Selektives Überschreiben ist der Grund für die Vererbung. Wenn Sie alles überschreiben wollen, ist das ein seltsamer Sonderfall.
S.Lott
1
Wer würde alles außer Kraft setzen? Sie können an Python denken, als
wären
1
@bashmohandes: Ich würde niemals alles überschreiben. Die Frage zeigt jedoch einen entarteten Fall, in dem alles außer Kraft gesetzt wird. Dieser seltsame Sonderfall ist die Grundlage für die Frage. Da es im normalen OO-Design nie vorkommt, ist die Frage irgendwie sinnlos.
S.Lott
7

Ich denke, dass es sehr schwierig ist, mit solchen abstrakten Beispielen eine aussagekräftige, konkrete Antwort zu geben ...

Zur Vereinfachung gibt es zwei Arten der Vererbung: Schnittstelle und Implementierung. Wenn Sie die Implementierung erben müssen, unterscheidet sich Python nicht so stark von statisch typisierten OO-Sprachen wie C ++.

Bei der Vererbung der Benutzeroberfläche gibt es einen großen Unterschied, der meiner Erfahrung nach grundlegende Konsequenzen für das Design Ihrer Software hat. Sprachen wie Python zwingen Sie in diesem Fall nicht zur Verwendung der Vererbung, und das Vermeiden der Vererbung ist in den meisten Fällen ein guter Punkt, da es sehr schwierig ist, dort später eine falsche Entwurfsauswahl zu korrigieren. Das ist ein bekannter Punkt, der in jedem guten OOP-Buch angesprochen wird.

Es gibt Fälle, in denen die Verwendung der Vererbung für Schnittstellen in Python ratsam ist, z. B. für Plug-Ins usw. In diesen Fällen fehlt Python 2.5 und darunter ein "integrierter" eleganter Ansatz, und mehrere große Frameworks haben ihre eigenen Lösungen entwickelt (Zope, Trac, Twister). Python 2.6 und höher verfügt über ABC-Klassen, um dies zu lösen .

David Cournapeau
quelle
6

Es ist keine Vererbung, die das Eingeben von Enten sinnlos macht, sondern Schnittstellen - wie die, die Sie bei der Erstellung einer vollständig abstrakten Tierklasse ausgewählt haben.

Wenn Sie eine Tierklasse verwendet hätten, die ein reales Verhalten für ihre Nachkommen einführt, dann hätten Hunde- und Katzenklassen, die ein zusätzliches Verhalten eingeführt haben, einen Grund für beide Klassen. Nur wenn die Vorfahrenklasse keinen tatsächlichen Code zu den Nachkommenklassen beiträgt, ist Ihr Argument korrekt.

Da Python die Funktionen eines Objekts direkt kennen kann und diese Funktionen über die Klassendefinition hinaus veränderbar sind, ist die Idee, dem Programm mithilfe einer rein abstrakten Schnittstelle zu "sagen", welche Methoden aufgerufen werden können, etwas sinnlos. Aber das ist nicht der einzige oder sogar der Hauptpunkt der Vererbung.

Larry Lustig
quelle
5

In C ++ / Java / etc wird Polymorphismus durch Vererbung verursacht. Geben Sie diesen missverstandenen Glauben auf, und dynamische Sprachen öffnen sich Ihnen.

Im Wesentlichen gibt es in Python keine Schnittstelle, sondern "das Verständnis, dass bestimmte Methoden aufrufbar sind". Ziemlich handgewellt und akademisch klingend, nein? Dies bedeutet, dass Sie, da Sie "Sprechen" aufrufen, eindeutig erwarten, dass das Objekt eine "Sprechen" -Methode haben sollte. Einfach, oder? Dies ist insofern sehr Liskov-ian, als die Benutzer einer Klasse ihre Schnittstelle definieren, ein gutes Designkonzept, das Sie zu einer gesünderen TDD führt.

Was also übrig bleibt, ist, wie ein anderes Poster höflich vermeiden konnte, ein Code-Sharing-Trick. Sie könnten das gleiche Verhalten in jede "untergeordnete" Klasse schreiben, aber das wäre überflüssig. Einfachere Vererbung oder Einmischung von Funktionen, die in der gesamten Vererbungshierarchie unveränderlich sind. Kleinerer DRY-er-Code ist im Allgemeinen besser.

Agileotter
quelle
1

Sie können die Vererbung in Python und so ziemlich jeder anderen Sprache umgehen. Es geht jedoch nur um die Wiederverwendung von Code und die Vereinfachung des Codes.

Nur ein semantischer Trick, aber nachdem Sie Ihre Klassen und Basisklassen erstellt haben, müssen Sie nicht einmal wissen, was mit Ihrem Objekt möglich ist, um zu sehen, ob Sie dies tun können.

Angenommen, Sie haben d, das ist ein Hund, der Tier untergeordnet hat.

command = raw_input("What do you want the dog to do?")
if command in dir(d): getattr(d,command)()

Wenn der vom Benutzer eingegebene Wert verfügbar ist, führt der Code die richtige Methode aus.

Auf diese Weise können Sie eine beliebige Kombination aus Säugetier-, Reptilien- und Vogel-Hybridmonstrosität erstellen, die Sie möchten, und jetzt können Sie festlegen, dass "Rinde!" während des Fliegens und Ausstrecken der gespaltenen Zunge und es wird richtig damit umgehen! Viel Spass damit!

Mandroid
quelle
1

Ich sehe nicht viel Sinn in der Vererbung.

Jedes Mal, wenn ich Vererbung in realen Systemen verwendet habe, wurde ich verbrannt, weil dies zu einem Wirrwarr von Abhängigkeiten führte, oder ich erkannte einfach mit der Zeit, dass ich ohne sie viel besser dran wäre. Jetzt vermeide ich es so weit wie möglich. Ich habe einfach nie eine Verwendung dafür.

class Repeat:
    "Send a message more than once"
    def __init__(repeat, times, do):
        repeat.times = times
        repeat.do = do

    def __call__(repeat):
        for i in xrange(repeat.times):
             repeat.do()

class Speak:
    def __init__(speak, animal):
        """
        Check that the animal can speak.

        If not we can do something about it (e.g. ignore it).
        """
        speak.__call__ = animal.speak

    def twice(speak):
        Repeat(2, speak)()

class Dog:
     def speak(dog):
         print "Woof"

class Cat:
     def speak(cat):
         print "Meow"

>>> felix = Cat()
>>> Speak(felix)()
Meow

>>> fido = Dog()
>>> speak = Speak(fido)
>>> speak()
Woof

>>> speak.twice()
Woof

>>> speak_twice = Repeat(2, Speak(felix))
>>> speak_twice()
Meow
Meow

James Gosling wurde einmal auf einer Pressekonferenz eine Frage in der Art gestellt: "Wenn Sie zurückgehen und Java anders machen könnten, was würden Sie weglassen?". Seine Antwort war "Klassen", über die gelacht wurde. Er meinte es jedoch ernst und erklärte, dass es wirklich nicht um Klassen ging, sondern um die Vererbung.

Ich betrachte es als eine Art Drogenabhängigkeit - es gibt Ihnen eine schnelle Lösung, die sich gut anfühlt, aber am Ende bringt es Sie durcheinander. Damit meine ich, dass es eine bequeme Möglichkeit ist, Code wiederzuverwenden, aber es erzwingt eine ungesunde Kopplung zwischen Kind- und Elternklasse. Änderungen am Elternteil können das Kind beschädigen. Das Kind ist für bestimmte Funktionen vom übergeordneten Element abhängig und kann diese Funktionalität nicht ändern. Daher ist die vom Kind bereitgestellte Funktionalität auch an das Elternteil gebunden - Sie können nur beides haben.

Besser ist es, eine einzelne Client-Klasse für eine Schnittstelle bereitzustellen, die die Schnittstelle implementiert, wobei die Funktionalität anderer Objekte verwendet wird, die zur Konstruktionszeit erstellt werden. Wenn Sie dies über ordnungsgemäß gestaltete Schnittstellen tun, kann jede Kopplung beseitigt werden, und wir bieten eine hochkompositive API (dies ist nichts Neues - die meisten Programmierer tun dies bereits, nur nicht genug). Beachten Sie, dass die implementierende Klasse die Funktionalität nicht einfach verfügbar machen darf, andernfalls sollte der Client die zusammengesetzten Klassen nur direkt verwenden - er muss etwas Neues tun, indem er diese Funktionalität kombiniert.

Es gibt das Argument des Vererbungslagers, dass reine Delegierungsimplementierungen darunter leiden, weil sie viele "Klebemethoden" erfordern, die einfach Werte durch eine Delegierungskette weitergeben. Dies bedeutet jedoch lediglich, ein vererbungsähnliches Design mithilfe der Delegierung neu zu erfinden. Programmierer mit zu vielen Jahren Erfahrung mit vererbungsbasierten Designs sind besonders anfällig dafür, in diese Falle zu tappen, da sie, ohne es zu merken, darüber nachdenken, wie sie etwas mithilfe von Vererbung implementieren und es dann in Delegierung umwandeln würden.

Für eine ordnungsgemäße Trennung von Bedenken wie dem obigen Code sind keine Klebemethoden erforderlich, da jeder Schritt tatsächlich einen Mehrwert darstellt. Es handelt sich also überhaupt nicht um Klebemethoden (wenn sie keinen Mehrwert bieten, ist das Design fehlerhaft).

Es läuft darauf hinaus:

  • Für wiederverwendbaren Code sollte jede Klasse nur eines tun (und es gut machen).

  • Durch Vererbung werden Klassen erstellt, die mehr als eine Aufgabe erfüllen, da sie mit übergeordneten Klassen verwechselt werden.

  • Daher führt die Verwendung der Vererbung dazu, dass Klassen schwer wiederzuverwenden sind.

Mike A.
quelle
1

Ein weiterer kleiner Punkt ist, dass Sie im 3. Beispiel von op isinstance () nicht aufrufen können. Wenn Sie beispielsweise Ihr drittes Beispiel an ein anderes Objekt übergeben, das Anrufe entgegennimmt und "Tier" eingibt, sprechen Sie darüber. Wenn Sie dies nicht tun, müssen Sie nach Hundetyp, Katzentyp usw. suchen. Ich bin mir nicht sicher, ob die Instanzprüfung aufgrund der späten Bindung wirklich "Pythonic" ist. Aber dann müsste man so implementieren, dass das AnimalControl nicht versucht, Cheeseburger-Typen in den Truck zu werfen, weil Cheeseburger nicht sprechen.

class AnimalControl(object):
    def __init__(self):
        self._animalsInTruck=[]

    def catachAnimal(self,animal):
        if isinstance(animal,Animal):
            animal.speak()  #It's upset so it speak's/maybe it should be makesNoise
            if not self._animalsInTruck.count <=10:
                self._animalsInTruck.append(animal) #It's then put in the truck.
            else:
                #make note of location, catch you later...
        else:
            return animal #It's not an Animal() type / maybe return False/0/"message"
yedevtxt
quelle
0

Klassen in Python sind im Grunde nur Möglichkeiten, eine Reihe von Funktionen und Daten zu gruppieren. Sie unterscheiden sich von Klassen in C ++ und dergleichen.

Ich habe meistens Vererbung gesehen, die zum Überschreiben von Methoden der Superklasse verwendet wurde. Zum Beispiel wäre vielleicht eine pythonischere Verwendung der Vererbung ..

from world.animals import Dog

class Cat(Dog):
    def speak(self):
        print "meow"

Natürlich sind Katzen keine Art von Hund, aber ich habe diese DogKlasse (von Drittanbietern) , die perfekt funktioniert, mit Ausnahme der speakMethode, die ich überschreiben möchte - dies erspart die erneute Implementierung der gesamten Klasse, nur damit sie miaut. Auch hier Catist zwar keine Art von Dog, aber eine Katze erbt viele Attribute.

Ein viel besseres (praktisches) Beispiel für das Überschreiben einer Methode oder eines Attributs ist das Ändern des Benutzeragenten für urllib. Sie unterteilen urllib.FancyURLopenerund ändern grundsätzlich das Versionsattribut ( aus der Dokumentation ):

import urllib

class AppURLopener(urllib.FancyURLopener):
    version = "App/1.7"

urllib._urlopener = AppURLopener()

Eine andere Art und Weise, wie Ausnahmen verwendet werden, ist für Ausnahmen, wenn die Vererbung "richtiger" verwendet wird:

class AnimalError(Exception):
    pass

class AnimalBrokenLegError(AnimalError):
    pass

class AnimalSickError(AnimalError):
    pass

..Sie können dann fangen AnimalError, um alle Ausnahmen zu fangen, die davon erben, oder eine bestimmte wie AnimalBrokenLegError

dbr
quelle
6
Ich bin… ein bisschen verwirrt von Ihrem ersten Beispiel. Zuletzt habe ich überprüft, dass Katzen keine Art von Hund sind, daher bin ich mir nicht sicher, welche Beziehung Sie demonstrieren möchten. :-)
Ben Blank
1
Sie spielen mit dem Liskov-Prinzip: Die Katze ist kein Hund. In diesem Fall kann es in Ordnung sein, es zu verwenden, aber was ist, wenn sich die Hundeklasse ändert und beispielsweise ein "Blei" -Feld erhält, das für Katzen sinnlos ist?
Dmitry Risenberg
1
Wenn es keine Tier-Basisklasse gibt, besteht Ihre Alternative darin, das Ganze neu zu implementieren. Ich sage nicht, dass dies die beste Vorgehensweise ist (wenn es eine Tier-Basisklasse gibt, verwenden Sie sie), aber sie funktioniert und wird häufig verwendet ( Dies ist die empfohlene Methode zum Ändern des Benutzeragenten von urllib (siehe Beispiel, das ich hinzugefügt habe)
dbr