Wie packt ein Affe eine Funktion in Python?
Ich habe Probleme beim ersetzen einer Funktion aus einem anderen Modul mit einer anderen Funktion, und es macht mich verrückt.
Sagen wir, ich habe ein Modul bar.py das sieht dann so aus:
from a_package.baz import do_something_expensive
def a_function():
print do_something_expensive()
Und ich habe ein anderes Modul, das wie folgt aussieht:
from bar import a_function
a_function()
from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'
a_function()
import a_package.baz
a_package.baz.do_something_expensive = lambda: 'Something really cheap.'
a_function()
Ich würde erwarten, dass die Ergebnisse bekommen:
Something expensive!
Something really cheap.
Something really cheap.
Aber stattdessen bekomme ich diese:
Something expensive!
Something expensive!
Something expensive!
Was mache ich falsch?
InformationsquelleAutor der Frage guidoism | 2010-03-03
Du musst angemeldet sein, um einen Kommentar abzugeben.
Kann es helfen, denken Sie daran, wie Python namespaces arbeiten: Sie sind im wesentlichen Wörterbücher. Also, wenn Sie dies tun:
betrachten Sie es wie folgt:
Ich hoffe, Sie können erkennen, warum dies nicht funktioniert, dann 🙂 nach dem import einen Namen in einem Namensraum, der Wert der Namen in den Namensraum importiert von ist irrelevant. Du bist nur die änderung des Wertes der do_something_expensive in der lokalen Modul-namespace oder in a_package.baz-namespace vor. Aber weil bar Importe do_something_expensive direkt, anstatt auf Sie verweisen aus der Modul-namespace, den Sie schreiben müssen, um den namespace:
InformationsquelleAutor der Antwort Nicholas Riley
Es ist eine wirklich elegante decorator: Guido van Rossum: Python-Dev-Liste: Monkeypatching Idiome.
Gibt es auch die dectools Paket, das ich sah, eine PyCon 2010, die möglicherweise verwendet werden in diesem Zusammenhang auch, aber das könnte tatsächlich gehen Sie den anderen Weg (monkeypatching an der Methode deklarative Ebene... wo du nicht bist)
InformationsquelleAutor der Antwort RyanWilcox
In der erste Schnipsel, den Sie machen
bar.do_something_expensive
finden Sie die function-Objekt, dassa_package.baz.do_something_expensive
bezieht sich auf in diesem moment. Wirklich "monkeypatch", die Sie benötigen, um die Funktion zu ändern sich (Sie ändern nur, was Namen bezeichnen); dies ist möglich, aber Sie eigentlich gar nicht tun wollen.In Ihre versuche, ändern Sie das Verhalten der
a_function
getan haben Sie zwei Dinge:Beim ersten Versuch, den Sie machen, do_something_expensive einen globalen Namen in Ihrem Modul. Jedoch, Sie rufen
a_function
die nicht Aussehen in Ihrem Modul, um Namen aufzulösen, so dass es bezieht sich noch auf die gleiche Funktion.In dem zweiten Beispiel, das Sie ändern, was
a_package.baz.do_something_expensive
bezieht, aberbar.do_something_expensive
ist nicht auf Magische Weise an Sie gebunden. Dieser name bezieht sich noch auf die Funktion Objekt sah es, wenn it war initilized.Die einfachste, aber weit von ideal Ansatz wäre die änderung
bar.py
zu sagenDie richtige Lösung ist wahrscheinlich eines von zwei Dingen:
a_function
zu nehmen, eine Funktion, der als argument nennen, dass, anstatt zu versuchen zu schleichen, und ändern, welche Funktion es ist hart codiert zu finden, oderVerwendung von globals (das ist das was ändern Modul-level " - Sachen aus den anderen Modulen ist) ist ein schlechte Sache das führt zu wartbaren, verwirrend, untestestable, unbezwingbar code der flow ist schwer zu verfolgen.
InformationsquelleAutor der Antwort Mike Graham
Wenn du willst, nur patch es auf Ihren Anruf und lassen Sie andernfalls das original-code, den Sie verwenden können, https://docs.python.org/3/library/unittest.mock.html#patch (seit Python 3.3):
InformationsquelleAutor der Antwort Risadinha
do_something_expensive
ima_function()
Funktion ist nur eine variable im Namensraum des Moduls zeigen, um ein function-Objekt. Wenn Sie neu definieren, das Modul, das Sie es tun in einen anderen namespace.InformationsquelleAutor der Antwort DavidG