2015-09-28 10 views
6

Lets sagen, dass ich die folgenden zwei Klassen in Modul haben aPython: wie Affen-Patch (swap) Klassen

class Real(object): 
    ... 
    def print_stuff(self): 
     print 'real' 

class Fake(Real): 
    def print_stff(self): 
     print 'fake' 

in Modul b verwendet es die Real Klasse

from a import Real 
Real().print_stuff() 

Wie kann ich Affe patch, so dass, wenn b importiert Real ist es tatsächlich mit Fake ausgetauscht?

Ich habe versucht, so in Skript initialisieren, aber es funktioniert nicht.

if env == 'dev': 
    from a import Real, Fake 
    Real = Fake 

Mein Zweck ist es, die Fake-Klasse im Entwicklungsmodus zu verwenden.

+0

Was ist dieses Initialisierungsskript, es ist ein anderes '.py', das am Anfang Ihres ursprünglichen Programms läuft? –

+0

Ja, es ist eine Django-App, also ist das Init-Skript in app.py, ich habe das nicht erwähnt, um generisch zu sein. –

Antwort

2

Das Problem ist, dass, wenn Sie zu tun -

from a import Real, Fake 

Sie importieren grundsätzlich diese beiden Klassen in Ihren initialize Skript Namensraum und die Schaffung von Real und Fake Namen in der initialize Skript Namespace . Dann machen Sie den Namen Real in initialize Skriptpunkt auf Fake, aber das ändert nichts in der tatsächlichen a Modul.

Wenn initialize Skript ist ein weiterer .py Modul/script bei Läufen am Anfang des ursprünglichen Programms, dann können Sie die unten verwenden -

if env == 'dev': 
    import a 
    a.Real = a.Fake 

Bitte beachten Sie, dies würde a.Real machen auf die Fake Klasse beziehen Wann immer Sie Real von a Modul verwenden, nachdem die obige Zeile ausgeführt wird.


Obwohl ich würde vorschlagen, dass ein besserer Weg, dies selbst in Ihrem a Modul zu tun wäre, indem sie es möglich, die env in diesem Modul zu prüfen, wie -

if <someothermodule>.env == 'dev': 
    Real = Fake 

Wie in den Kommentaren gefragt wurde -

importiert nicht auch ein Import in Skript Namespace initialisieren? Was ist der Unterschied zwischen dem Importieren von Modulen und Klassen?

Die Sache ist die, dass, wenn importieren Sie einfach die Klasse mit from a import class, was Sie eigentlich tun, ist diese Variable erstellen, class in Ihrem Modul Namensraum (in dem Modul, das Sie es zu importieren), diese Variable zu ändern, um zu zeigen Etwas Neues in diesem Modul-Namespace hat keine Auswirkungen auf die ursprüngliche Klasse in ihrem ursprünglichen Modul-Objekt, sondern nur auf das Modul, in dem sich das Modul geändert hat.

Aber wenn man import a tun, die Sie importieren, nur das Modul a (und während das Modul-Objekt importieren auch im sys.modules Wörterbuch zwischengespeichert wird, so dass alle anderen Importe a von anderen Modulen würde diese im Cache gespeicherte Version von sys.modules erhalten) (Eine weitere Anmerkung ist, dass from a import something auch intern importiert a und speichert es in sys.modules, aber lassen Sie uns nicht in diese Details kommen, wie ich denke, dass hier nicht notwendig ist).

Und dann, wenn Sie a.Real = <something> tun, ändern Sie das Real Attribut a Modul-Objekt, das auf die Klasse verweist, auf etwas anderes, das mutiert das a Modul direkt, damit die Änderung auch reflektiert wird, wenn das Modul a wird von einem anderen Modul importiert.

+0

Wenn ich das in einem "a" -Modul mache, wäre es im Modul "a" vergraben und es wäre schwierig für andere Leute zu erkennen, was vor sich geht, stimmst du zu? Besonders Modul 'a' ist eine Bibliothek. –

+0

Ja, möglich, wenn 'a' sehr sehr groß ist, aber ich würde sagen, es wäre schwieriger für die Leute zu erkennen, dass ihr 'init'-Modul mit' a 'spielt, anstatt dass das Problem in' a' selbst liegt. Sagen wir, ich bin ein neuer Entwickler, ich habe den Code ausgeführt und sehe, dass ich eine falsche Klasse bekomme, wenn ich versuche, 'Real' von' a' zu nennen, ich überprüfe den Code in 'a' und nicht, wo ich das finde 'wird von' Fake' überschrieben, es wäre sehr schwer für mich zu erraten/herauszufinden, dass 'init' Modul ist, was die' a.Real' in 'a.Fake' ändert. Sind Sie einverstanden? –

+0

Ich bin ziemlich noob auf Python-Importe, importiert nicht auch Import in 'initialize' Skript-Namespace? Was ist der Unterschied zwischen dem Importieren von Modulen und Klassen? –

3

Sie können patch aus dem mock Modul verwenden. Hier ein Beispiel:

with patch('yourpackage.b.Real') as fake_real: 
    fake_real.return_value = Fake() 
    foo = b.someClass() 
    foo.somemethod() 
+0

Funktioniert Ihr Code nicht nur in seinem Kontext? Ich bin in diesem Fall nicht für Unit-Test-Zwecke flicken. –