2016-05-19 15 views
0

Ich sehe nicht, dass es wichtig ist, weil die realen Implementierungen in jeder Unterklasse sowieso implementiert werden. Betrachten Sie diesen Code:Die Notwendigkeit einer nicht implementierten Methode in der abstrakten Klasse wegen des Polymorphismus?

class Cat: 
    def __init__(self, breed): 
     self.breed = breed 
    def sound(self): # having this or not does'nt make any difference 
     pass 

class Angora(Cat): 
    def sound(self): 
     print("miau") 

class Scotish(Cat): 
    def sound(self): 
     print('meowww') 

a = Angora('angora') 
b = Scotish('scotish') 

cats = [a, b] 

for catt in cats: 
    catt.sound() 

Ich muss etwas jetzt vermissen, weil ich Schwierigkeiten habe, diesen Begriff zu begreifen.

+1

Schauen Sie sich das 'abc' Modul an, es erlaubt eine Variation davon, die eine nützliche Fehlerprüfung bietet. –

Antwort

1

eine leere Methode platzieren, die, erhalten außer Kraft gesetzt wird kein ist es nicht viel Sinn, aber wenn Cat eine tatsächliche abstrakte Klasse war und sound war ein Methode, die inforced wurde zu außer Kraft gesetzt, dann gibt es einen großen Nutzen daraus, zum Beispiel:

import abc 

class Cat(abc.ABC): 
    def __init__(self, breed): 
     self.breed = breed 
    @abc.abstractmethod 
    def sound(self): # having this as an abstract method DOES make a difference 
     pass 

class Scotish(Cat): 
    def sonud(self): 
     print('meowww') 

Ob Sie die Typo oder nicht bemerkt haben, werden Sie diese Fehlermeldung erhalten, wenn eine Scotish zu initialisieren versuchen :

Also bevor Sie irgendwelche seltsamen mehrdeutigen Fehler bekommen, erhalten Sie, was tatsächlich schief ging sofort!

Auch im Gegensatz zu einfach raise NotImplementedError kann eine abstrakte Methode, gut abstrakten Code enthalten! Wie ein Beispiel dafür, was die Funktion tun könnte, wäre ein großartiges Beispiel _collections_abc.Generator.throw sein:

@abstractmethod 
def throw(self, typ, val=None, tb=None): 
    """Raise an exception in the coroutine. 
    Return next yielded value or raise StopIteration. 
    """ 
    if val is None: 
     if tb is None: 
      raise typ 
     val = typ() 
    if tb is not None: 
     val = val.with_traceback(tb) 
    raise val 

Dies ist nicht wirklich Code, das ist nicht, wie ein Generator tatsächlich behandelt die throw Methode, aber es ist eine großes abstrakten von wie man mit den Argumenten zumindest fertig wird!

2

Sie brauchen so etwas, also, wenn Sie vergessen haben, die Methode in einer Unterklasse zu implementieren, sehen Sie eine Ausnahme.

class Cat: 
    def __init__(self, breed): 
     self.breed = breed 

    def sound(self): 
     raise NotImplementedError('Should be implemented in subclass') 

Auch macht es Klassenschnittstelle sauberer.

+0

Sie erhalten trotzdem eine Ausnahme, wenn 'Cat' keinen' Sound' hat. Es ist ein 'AttributeError', der ungefähr so ​​aussagekräftig ist wie der von Ihnen verwendete Fehler. – user2357112

+0

@ user2357112 siehe [mein Szenario mit dem Tippfehler] (http://stackoverflow.com/a/37335195/5827215), in diesem Fall würden Sie nur bekommen AttributError: 'Scotish' Objekt hat kein Attribut 'Sound'', die kann Es ist wirklich verwirrend, wenn du den Tippfehler nicht siehst, wenn dir etwas spezifisch sagt, dass es in einer Unterklasse nicht richtig überschrieben wurde, was zumindest schränkt, was falsch gelaufen ist. –

+0

@ user2357112 'NotImplementedError' sagt, dass Sie vergessen, etwas zu implementieren, im Gegensatz zu' AttributeError' – dizballanze

1

Auch wenn es nicht erforderlich ist, damit der Code funktioniert, ist es hilfreich, den Code selbstdokumentierend zu machen, damit die Implementierung eines Cat in Zukunft einfacher wird. Ich mache es auch möglich, introspection/reflection auf einem Cat-Objekt zu verwenden. Siehe auch die Beweggründe für ‚richtige‘ abstrakte Basisklassen Hinzufügen hier Python: https://www.python.org/dev/peps/pep-3119/

0

Die Sache, die bisher nicht gesagt wurde:

dies ist ein wesentliches Element Klanglösungen in Bezug der open closed principle umzusetzen.

Der entscheidende Punkt ist: In der Regel verwenden Sie abstrakte Basisklassen, um ein bestimmtes Verhalten zu implementieren ... das für alle Unterklassen gleich sein sollte. Auf der anderen Seite muss diese "übliche" Implementierung möglicherweise unterklassenspezifische Methoden aufrufen, um ihre Aufgabe zu erfüllen.

Einige Pseudo-Code Beispiel:

abstract class Base: 
    final foo(): 
     return "foo" + infoFromChild() 

    abstract infoFromChild() 

Dann:

  • Sie "foo()" auf ein beliebiges Objekt aufrufen kann, die von Basis erbt „
  • Sie die leere abstrakte außer Kraft setzen kann infoFromChild "In jeder Kindklasse zur entsprechenden Sache

Aber natürlich: solche Sachen ma viel mehr "Sinn" in "mehr statischen" Sprachen wie Java oder C++. Innerhalb von Python könnten Sie die Dinge trotzdem anders machen.