2009-05-25 3 views
16

Lassen Sie uns sagen, dass ich die folgende KlasseEine saubere Art und Weise eine Klasse Attribut in Subklassen der Erweiterung

class Parent(object): 
    Options = { 
     'option1': 'value1', 
     'option2': 'value2' 
    } 

Und eine Unterklasse namens Child

class Child(Parent): 
    Options = Parent.Options.copy() 
    Options.update({ 
     'option2': 'value2', 
     'option3': 'value3' 
    }) 

Ich möchte in der Lage, außer Kraft zu setzen oder fügen Sie Optionen in die Kindklasse. Die Lösung, die ich verwende, funktioniert. Aber ich bin mir sicher, dass es einen besseren Weg gibt.


EDIT

Ich will nicht Optionen hinzufügen als Klassenattribute, weil ich andere Klasse Attribute, die keine Optionen sind, und ich ziehe alle Optionen an einem Ort zu halten. Dies ist nur ein einfaches Beispiel, der eigentliche Code ist komplizierter.

+0

Warum die Kopie/Update-Logik in einem Verfahren der Elternklasse nicht versenden? Wenn Sie es in "__init__" schreiben und immer 'super() .__ init __()' am Ende der Unterklasse '__init__' nennen, wie Sie sollten, sollte es funktionieren. – asmeurer

Antwort

8

Eine Möglichkeit wäre, Keyword-Argumente verwenden, um DIKT zusätzliche Tasten zu spezifizieren:

Parent.options = dict(
    option1='value1', 
    option2='value2', 
) 

Child.options = dict(Parent.options, 
    option2='value2a', 
    option3='value3', 
) 

Wenn Sie schicker erhalten mag, dann das Deskriptor-Protokoll verwenden, können Sie ein Proxy-Objekt erstellen Das wird die Suche einkapseln. (Gehen Sie einfach mit dem Eigentümer .__ mro__ vom Besitzerattribut zur Methode __get __ (self, instance, owner)). Oder sogar Züchter, die in das wahrscheinlich-nicht-eine-gute-Idee-Territorium eindringen, Metaklassen/Klassenausstatter.

+0

Dies funktioniert nur, wenn Ihre Schlüssel alle Zeichenfolgen sind, die gültige Python-Variablenbezeichner sind. – asmeurer

4

Warum nicht einfach Klassenattribute verwenden?

class Parent(object): 
    option1 = 'value1' 
    option2 = 'value2' 

class Child(Parent): 
    option2 = 'value2' 
    option3 = 'value3' 
+0

Sie, gewinnen. Beat mich um 20 Sekunden :) – SpliFF

+0

Ich bevorzuge Optionen in einem Attribut zu halten, da ich andere Klassenattribute habe, die keine Optionen sind. –

+0

Sie können spezielle Attribute mit einem Präfix markieren, z. "opt_foo" und "opt_bar" und dann auf diese z. '[getattr (self, x) für x in dir (self) wenn x.startswith ("opt _")] –

2
class ParentOptions: 
    option1 = 'value1' 
    option2 = 'value2' 

class ChildOptions(ParentOptions): 
    option2 = 'value2' 
    option3 = 'value3' 

class Parent(object): 
    options = ParentOptions() 

class Child(Parent): 
    options = ChildOptions() 
+0

Hmmm, ich weiß es nicht. Ich bevorzuge einen automatisierten Weg. Ich habe Dutzende von Unterklassen. Das Hinzufügen einer separaten Klasse für jede Option wird ein Durcheinander sein. –

21

semantisch äquivalent zu Ihrem Code aber wohl ordentlicheres:

class Child(Parent): 
    Options = dict(Parent.Options, 
     option2='value2', 
     option3='value3') 

Denken Sie daran, „Leben ohne Zahnspange besser ist“, und durch dict Aufruf explizit können Sie oft Klammern vermeiden (und zusätzliche Anführungszeichen um Schlüssel, die konstante Kennung sind wie Strings).

Siehe http://docs.python.org/library/stdtypes.html#dict für weitere Details - der Schlüssel Bit „Wenn ein Schlüssel sowohl im Positions Argumente und als Schlüsselwort-Argument angegeben ist, wird der Wert mit dem Schlüsselwort zugeordnet ist, beibehalten“, dh Schlüsselwort args Überschreibung Tasten- Werteverknüpfungen im Positionsarg, genau wie die Methode update können Sie diese überschreiben).

6

Nach mehr darüber nachzudenken, und dank @SpliFF Vorschlag ist das, was ich kam mit:

class Parent(object): 
    class Options: 
     option1 = 'value1' 
     option2 = 'value2' 


class Child(Parent): 
    class Options(Parent.Options): 
     option2 = 'value2' 
     option3 = 'value3' 

Ich bin zwar zu besseren Lösungen noch offen.

+0

Der Nachteil dieses Ansatzes besteht darin, dass es viel hässlicher ist, dynamische Einstellungen vorzunehmen. Zum Beispiel, um die Optionen einer Klasse aus einer Konfigurationsdatei zu lesen. –

+0

Nun, diese Lösung passt mehr in meinen speziellen Fall. Aber als allgemeine Lösung ist Ihre Lösung besser. –

+0

Die entsprechenden '__getitem__' /' __setitem__' Methoden können später bei Bedarf hinzugefügt werden. Dies ist eine gute, saubere Lösung. –

2

Hier wird mit einem Weg, um eine Metaklasse

class OptionMeta(type): 
    @property 
    def options(self): 
     result = {} 
     for d in self.__mro__[::-1]: 
      result.update(getattr(d,'_options',{})) 
     return result 

class Parent(object): 
    __metaclass__ = OptionMeta 
    _options = dict(
     option1='value1', 
     option2='value2', 
     ) 

class Child(Parent): 
    _options = dict(
     option2='value2a', 
     option3='value3', 
     ) 

print Parent.options 
print Child.options 
Verwandte Themen