2016-10-06 1 views
4

Wie kann ich die __init__ eine Basisklasse erweitern, mehr Argumente in dem, ohne super().__init__(foo, bar) erfordern in jeder abgeleiteten Klasse analysiert werden?Weitere Argumente in abgeleiteten Klasse __init__ als Basisklasse __init__

class Ipsum: 
    """ A base ipsum instance. """ 

    def __init__(self, foo, bar): 
     self.foo = flonk(foo) 
     grungole(self, bar) 
     self._baz = make_the_baz() 


class LoremIpsum(Ipsum): 
    """ A more refined ipsum that also lorems. """ 

    def __init__(self, foo, bar, dolor, sit, amet): 
     super().__init__(foo, bar) 
     farnark(sit, self, amet) 
     self.wibble(dolor) 

Der Zweck des Beispiels zu zeigen, dass es erhebliche Verarbeitung in den Ipsum.__init__ passiert ist, so sollte es nicht in jeder Unterklasse dupliziert werden; und die LoremIpsum.__init__ benötigt die foo und bar Parameter genauso verarbeitet wie Ipsum, hat aber auch ihre eigenen speziellen Parameter.

Es zeigt auch, dass, wenn Ipsum Bedarf eine andere Signatur, jede abgeleiteten Klasse auch nicht nur seine Unterschrift ändern muss akzeptieren geändert werden, aber wie nennt es die übergeordnete Klasse __init__. Das ist inakzeptabel fragil.

Stattdessen würde Ich mag, wie etwas zu tun ist:

class Ipsum: 
    """ A base ipsum instance. """ 

    def __init__(self, foo, bar, **kwargs): 
     self.foo = flonk(foo) 
     grungole(self, bar) 
     self._baz = make_the_baz() 

     self.parse_init_kwargs(kwargs) 

    def parse_init_kwargs(self, kwargs): 
     """ Parse the remaining kwargs to `__init__`. """ 
     pass 


class LoremIpsum(Ipsum): 
    """ A more refined ipsum that also lorems. """ 

    def parse_init_kwargs(self, kwargs): 
     (dolor, sit, amet) = (kwargs['dolor'], kwargs['sit'], kwargs['amet']) 
     farnark(sit, self, amet) 
     self.wibble(dolor) 

, die den großen Vorteil hat, dass LoremIpsum brauchen nur die Teile tun, die zu dieser Klasse etwas Besonderes sind; Handhabung Ipsum Argumente wird von dieser Klasse __init__ ohne zusätzlichen Code behandelt.

Der Nachteil ist jedoch offensichtlich: Dies ist effektiv die Umsetzung der benannten Parameter durch die Übergabe eines Wörterbuchs um implementieren. Es vermeidet viel Wiederholung, ist aber nicht sehr klar.

Welche Tools verfügbar sind, um die Unterklassendefinitionen zu vermeiden immer benötigen die foo und bar Parameter zu erklären, und immer benötigen super().__init__(foo, bar) anrufen? Die sind leicht zu bekommen, so dass es besser wäre, wenn sie nicht benötigt würden und einfach automatisch passieren könnten, während immer noch LoremIpsum Anpassung der Initialisierung.

Antwort

2

Der üblicher Weg, diese ist in etwa wie folgt zu schreiben:

class Ipsum: # 3.x-ism; in 2.x always inherit from object. 
    def __init__(self, arg1, arg2, arg3): 
     # etc... 

class LoremIpsum(Ipsum): 
    def __init__(self, arg4, arg5, *args, **kwargs): 
     super().__init__(*args, **kwargs) # 3.x-ism; in 2.x super() needs arguments. 
     # Do stuff with arg4 and arg5 

Diese nicht abgeleiteten Klassen zu modifizieren erfordern, wenn die Basisklasse Signaturen ändert. Sie müssen immer noch alles ändern, was Ihre Basisklasse direkt instanziiert, so dass Sie die Signatur nicht häufig ändern möchten.

Dieser Ansatz ist auch besser, da er sich im Allgemeinen bei Mehrfachvererbung mehr oder weniger korrekt verhält, auch wenn Ihre abgeleitete Klasse in der Reihenfolge der Methodenauflösung vor eine andere abgeleitete Klasse gestellt wird und Sie noch nie davon gehört haben diese andere Klasse (mit anderen Worten, auch wenn super().__init__() eine Methode aufruft, von der Sie nichts wissen). Dies wird ausführlich in Hettinger super() considered super Blogpost diskutiert.

+0

Wahrscheinlich ist es hier keine gute Idee, '* args' zu übergeben. Die Argumente können sich ändern und viel Verwirrung stiften. Sollte stattdessen Kwargs verwenden. –

4

Ein flexibler Ansatz besteht darin, dass jede Methode im Vorfahrenbaum kooperativ entworfen wurde, um Schlüsselwortargumente und ein Schlüsselwortargumentwörterbuch zu akzeptieren, alle benötigten Argumente zu entfernen und die verbleibenden Argumente mithilfe von **kwds weiterzuleiten und das Wörterbuch zu verlassen leer für den letzten Aufruf in der Kette.

jeder Ebene Streifen-off das Schlüsselwort Argumente, dass es so, dass die endgültige leer dict ein Verfahren werden können, die überhaupt keine Argumente gesendet muss erwartet (beispielsweise Objekt .__ init__ erwartet Null Argumente):

class Shape: 
    def __init__(self, shapename, **kwds): 
     self.shapename = shapename 
     super().__init__(**kwds)   

class ColoredShape(Shape): 
    def __init__(self, color, **kwds): 
     self.color = color 
     super().__init__(**kwds) 

cs = ColoredShape(color='red', shapename='circle') 

Weitere Informationen zu diesem Ansatz finden Sie im Abschnitt "Praktische Hinweise" des Blogposts Super Considered Super oder im entsprechenden Artikel unter Pycon video.

class Ipsum: 
    """ A base ipsum instance. """ 

    def __init__(self, foo, bar): 
     self.foo = flonk(foo) 
     grungole(self, bar) 
     self._baz = make_the_baz() 

class LoremIpsum(Ipsum): 
    """ A more refined ipsum that also lorems. """ 

    def __init__(self, dolor, sit, amet, **kwds): 
     super().__init__(**kwds) 
     farnark(sit, self, amet) 
     self.wibble(dolor) 

Eine Instanziierung wie folgt aussehen würde:

In Ihrem Beispiel der Code würde wie folgt aussehen

li = LoremIpsum(dolor=1, sit=2, amet=3, foo=4, bar=5) 

Hinweis, auf diese Weise können Sie zu Ihrem ursprünglichen Ziel, das Hinzufügen neuer Argumente erreichen entweder __init__ Methode, ohne die andere zu beeinflussen.

+0

Ein Nachteil dabei ist, dass ich sehe: jemand, der die Signatur der 'LoremImpsum .__ init__' Methode inspiziert, sieht die wichtigen Parameter' foo' und 'bar' nicht, die dort erwähnt werden. Es ist jedoch auch problematisch, sie in jeder abgeleiteten Klasse zu erwähnen. Deshalb hoffe ich auf einen Weg, der bedeutet, dass ich "__init__" nicht in jeder abgeleiteten Klasse neu implementieren muss. – bignose

+0

Wenn ich nur Python 3 (und nicht 2.x) verwende, würde ich vorschlagen, die Verwendung von Kwargs zu erzwingen - siehe http://StackOverflow.com/a/14298976/5766144 –

0

Abgeleitete Klassen müssen die Superklasse __init__ aufrufen. So funktioniert die objektorientierte Programmierung. Ist es spröde? Ja das kann sein; Aus diesem Grund sind tiefe oder breite Klassenhierarchien normalerweise keine gute Idee. Wenn die Operationen auf foo und bar in Lorem von Bedeutung sind (wie Sie sagen, dass sie sind), dann ist Lorem.__init__() korrekt zu fordern, eine Kosten, die bezahlt werden muss. Die Standardwerte für foo und bar (und alle neuen Parameter) sind das einzige Tool, das den Umfang der erforderlichen Textänderung ändern kann, aber die Standardwerte sind nicht immer angemessen, sodass die Abschwächung möglicherweise nicht zutrifft.

Verwandte Themen