2017-02-13 3 views
1

Ich habe Klassen wie folgt aus:Übergeordnete dict in der Unterklasse

class Parent(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value', 
    } 

class Child(Parent): 
    _defaults = { 
     'key2': 'value2', 
     'key3': 'value2', 
    } 

Wenn ich das Kind Klasse schaffen, ich will _defaults zu der übergeordneten Klasse _defaults und dann mit dem Kind _defaults aktualisiert eingestellt werden. So dass die tatsächliche aufgelöste Klasse soll wie folgt aussehen:

class Child(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value2', 
     'key3': 'value2', 
    } 

Ist dies möglich, mit einem __metaclass__ Konstruktor, oder was?

Antwort

1

eine Metaklasse verwenden, müssen Sie dies in __new__ Methode tun, um die Änderungen vor Instanziierung anzuwenden. Da die Methode __new__cls, name, bases, namespace, **kwds als Argument akzeptiert, können Sie einfach auf das übergeordnete Objekt zugreifen, indem Sie das Argument bases verwenden, das ein Tupel aller Basisklassen ist. Dann können Sie einfach Ihr erwartetes Attribut aktualisieren.

class MyMetaClass(type): 

    def __new__(cls, name, bases, namespace, **kwds): 
     bases[0]._defaults.update(namespace['_defaults']) 
     result = type.__new__(cls, name, bases, dict(namespace)) 
     return result 


class Parent(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value', 
    } 

class Child(Parent, metaclass=MyMetaClass): 
    _defaults = { 
     'key2': 'value2', 
     'key3': 'value2', 
    } 

Demo:

c = Child()  
print(type(c).__bases__[0]._defaults) 
{'key2': 'value2', 'key3': 'value2', 'key': 'value'} 

Beachten Sie, dass als @jsbueno diese Methode erwähnt ein großes Problem hat, die die an jeder Instanziierung _defaults Attribut des übergeordneten Klasse zu aktualisieren ist. Eine Möglichkeit, dieses Problem abzuweisen, besteht darin, der Meta-Klasse ein Flag zu geben, um das Parent nur einmal zu aktualisieren.

+0

Idealerweise würden Sie 'ChainMap' verwenden, wie in einer anderen Antwort angegeben, aber das funktioniert gut genug. – xaav

+1

@xaav Ja, das ist ein guter Ansatz. Aber ich habe diese Antwort gepostet, da ich nach einer metaklassenbasierten Lösung für einen Bildungszweck suche. – Kasramvd

+0

Ich bin derjenige, der die Frage gestellt hat! – xaav

2

Sie erreichen dies mit einem collections.ChainMap

import collections 

class Child(Parent): 
    _defaults = collections.ChainMap({ 
     'key2': 'value2', 
     'key3': 'value2', 
    }, Parent._defaults) 

Natürlich können Sie konnte eine Metaklasse schreiben, diese Dinge für Sie zu kombinieren, aber es würde schwierig sein (im Allgemeinen), um herauszufinden, welche Karten sollte mit den Karten auf den Oberklassen kombiniert werden und die sollte überschreiben ...

1

Lesen Sie die Wörterbuchelemente direkt, dann aktualisieren?

class Parent(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value', 
    } 


class Child(Parent): 
    _defaults = {k: v for k, v in Parent._defaults.items()} 
    _defaults.update({'key2': 'value2', 
         'key3': 'value2'}) 


testpar = Parent() 
test = Child() 

print(testpar._defaults) 
print(test._defaults) 

druckt:

{'key2': 'value', 'key': 'value'} 
{'key3': 'value2', 'key2': 'value2', 'key': 'value'} 

in beiden 2,7 und 3,5

+0

Warum nicht '_defaults = Parent._defaults.copy()'? – mgilson

Verwandte Themen