2017-12-11 2 views
1

Ich habe eine Datenzugriffsklasse, die die Daten aus der Datenbank abruft. Es ist als ein Mix implementiert, d. H. ReferenceData, ich möchte diese Klasse mit der MockReferenceData-Klasse austauschen, so dass ich im Komponententest nicht auf die DB zugreifen muss. Aber funktioniert nicht.Ersetzen Sie eine Python-Mixin-Klasse zur Laufzeit

* Hinweis: Ich kann die Abhängigkeitsinjektion nicht verwenden. Da das Team es nicht verwenden möchte, wird es eine sehr große Codeänderung geben. Also ich suche, Mixin zur Laufzeit zu ersetzen.

class MockReferenceData(object): 
    def dbName(self): 
    return 'mock' 

    def totalNumberOfSeats(self): 
    return '10' 

class ReferenceData(object): 
    def dbName(self): 
    return 'real DB' 

    def totalNumberOfSeats(self): 
    return 'Fetch from DB' 

class Car(ReferenceData): 
    def showNumberOfSeats(self): 
    print self.totalNumberOfSeats() 


class Train(ReferenceData): 
    def showNumberOfSeats(self): 
    print self.totalNumberOfSeats() 

c = Car() 
c.showNumberOfSeats() 
t = Train() 
t.showNumberOfSeats() 

def extend_instance(obj, cls): 
    """Apply mixins to a class instance after creation""" 
    base_cls = obj.__class__ 
    base_cls_name = obj.__class__.__name__ 
    obj.__class__ = type(base_cls_name, (base_cls, cls),{}) 

extend_instance(c, MockReferenceData) 
c.showNumberOfSeats() // output now should be 10 

Out Put ist:

Fetch from DB 
Fetch from DB 
Fetch from DB 

ich gehofft wurde, wie ich extend_instance Methode verwendet haben, um neue verspott Klasse Ausgang zu zeigen sein wird:

Fetch from DB 
Fetch from DB 
10 

Antwort

0

Wenn man sich die MRO der Klasse aussehen Car Sie sehen:

>>> Car.__mro__ 
(<class '__main__.Car'>, <class '__main__.ReferenceData'>, <type 'object'>) 

So werden Methoden zunächst auf Car aufsah, dann auf ReferenceData und schließlich auf object.

Vergleichen Sie dies mit dem MRO Ihrer neuen Klasse (I NewCar als Name für Klarheit verwenden):

>>> type('NewCar', (Car, MockReferenceData), {}).__mro__ 
(<class '__main__.NewCar'>, <class '__main__.Car'>, <class '__main__.ReferenceData'>, <class '__main__.MockReferenceData'>, <type 'object'>) 

Dadurch wird die HRG der Car Klasse enthält. Methoden werden hier noch zuerst auf Car und dann auf ReferenceData nachgeschlagen, so wie totalNumberOfSeats auf ReferenceData gefunden wird, wird die Implementierung aus MockReferenceData nicht verwendet.

Was könnten Sie tun, ist Ihre Mock-Klasse im MRO vor die Car Klasse einzufügen:

>>> type('NewCar', (MockReferenceData, Car), {}).__mro__ 
(<class '__main__.NewCar'>, <class '__main__.MockReferenceData'>, <class '__main__.Car'>, <class '__main__.ReferenceData'>, <type 'object'>) 

Jetzt werden Methoden in der MockReferenceData Klasse nachgeschlagen befirst und wenn sie nicht existieren dort zurückfallen zur vorherigen Version. Also diese extend_instance Methode sollte für diesen einfachen Fall arbeiten:

def extend_instance(obj, cls): 
    """Apply mixins to a class instance after creation""" 
    obj.__class__ = type(obj.__class__.__name__, (cls, obj.__class__),{}) 
0

Don‘ t Vererbung verwenden. Es ist in vielen Fällen eine überstrapazierte Abstraktion, und dies ist ein Beispiel. Indem Sie die Abhängigkeitsinjektion verwenden, können Sie einfach die tatsächliche DB als Standard verwenden, aber ersetzen Sie Ihr Backend durch ein gespottetes.

Bitte folgen Sie auch PEP8 für Kodierung-Konventionen in Python. Sie verwenden die "falsche" Einrücktiefe von 2, was zu Bearbeitungsproblemen für andere Entwickler wie mich führt. Gleiches gilt für Methodennamen.

+0

Hallo Deets, Problem ist Code-Basis ist sehr alt und sehr groß. Ich habe gebeten, DI zu verwenden, aber das Team ist nicht bereit, es zu verwenden. Also versuche ich Mixin zur Laufzeit zu ändern. Ist es möglich? – Rahul

+0

@Rahul wie Sie gesehen haben, ist es möglich. Ich würde es immer noch nicht tun.Wenn Sie Ihre Klassen so modifizieren, können Sie sich auf alle Arten von schwer zu debuggenden Problemen einstellen. Wenn dein Team darauf besteht, bestehen sie darauf, etwas Schlechtes zu tun. Eine andere Möglichkeit, Ihr Problem zu lösen, wäre, eine Unterklasse des Autos zu erstellen, die sowohl vom Mixin als auch vom ursprünglichen Auto erbt. Dies ist sicherer, da es eine explizite Klasse erstellt, anstatt den globalen Interpreterstatus zu ändern. – deets

+0

Dank @deets, ich stimme zu, es ist nicht der sauberste Weg. Aber wenn das Team nicht will, habe ich keine Optionen. Auch wenn wir die Klasse im Unit-Test so erweitern, denke ich, dass sie keine Auswirkungen haben wird, da wir Objekte im Setup erstellen und im Teardown zerstören. Was denken Sie? – Rahul

0

Dank @mata i auch das gleiche tat änderte ich den Auftrag und es funktionierte.

def extend_instance(obj, cls): 
    """Apply mixins to a class instance after creation""" 
    base_cls = obj.__class__ 
    base_cls_name = obj.__class__.__name__ 
    obj.__class__ = type(base_cls_name, (cls, base_cls),{}) 
Verwandte Themen