2016-07-03 15 views
5

Ich möchte eine Funktion in meinem Modul, um auf den lokalen Namespace des Skripts zuzugreifen und zu ändern, die es importiert. Dies würde Funktionen wie folgt aktivieren:Ändern Sie den Namespace des importierenden Skripts in Python

>>> import foo 
>>> foo.set_a_to_three() 
>>> a 
3 
>>> 

Ist dies in Python möglich?

+0

Was ist mit nur 'a = 3'? –

+1

Es ist nur ein dummes Beispiel. Ich werde komplexere Sachen machen, die von 'foo' kommen und von' foo' zugewiesen werden müssen. Ich weiß jedoch, wie die Python-Zuweisung normal funktioniert. –

+0

Nicht möglich, nein. Sie könnten eine Funktion veranlassen, die Globals zu ändern, aber sie kann nur auf die Globals des Moduls zugreifen, in dem sie definiert ist, nicht auf das, von dem sie ausgeführt wird. – poke

Antwort

3

Eine Antwort großzügigerweise von @omz in einem Slack Team zur Verfügung gestellt wurde:

import inspect 
def set_a_to_three(): 
    f = inspect.currentframe().f_back 
    f.f_globals['a'] = 3 

Dies hat den Vorteil über die __main__ Lösung bereitstellt, die es in mehreren Ebenen der Importe, zum Beispiel funktioniert, wenn a Einfuhren b, die importiert mein Modul , foo kann foob 's Globals, nicht nur a' s (glaube ich)

jedoch ändern, wenn ich richtig verstehe, der lokale Namensraum zu modifizieren ist kompliziert. Als jemand anderes wies darauf hin:

Es bricht, wenn Sie diese Funktion aus einer anderen Funktion aufrufen. Dann ist der nächste Namensraum auf dem Stapel ein gefälschtes Diktat der Einheimischen, an das man nicht schreiben kann. Nun, Sie können, aber Schreibvorgänge werden ignoriert.

Wenn es eine zuverlässigere Lösung gibt, würde das sehr geschätzt werden.

3

Damit das Modul auf den Namespace Ihres Skripts zugreifen kann, müssen Sie den Namespace an die Funktion übergeben. Zum Beispiel:

def set_a_to_three(globalDict, localDict): 
    localDict['a'] = 3 

Nebenbei bemerkt:

>>> import foo 
>>> foo.set_a_to_three(globals(), locals()) 
>>> a 
3 

Das Innere des Code wie folgt aussehen würde in diesem Fall das Globals Wörterbuch nicht erforderlich ist, aber in Fällen, in denen Sie benötigen globale Variable zu ändern, Sie müssen das Globals-Wörterbuch verwenden. Aus diesem Grund habe ich es hier eingefügt, um die Funktion erweiterbar zu machen.

Allerdings wäre es einfacher, einfach a = 3 zu setzen, wie Eli Sadoff sagte. Es ist keine gute Vorgehensweise, Namespaces anderen Programmen zu übergeben.

+0

Dies ist in vielen Fällen eine anständige Antwort. Meine Situation ist * etwas * komplizierter, da ich die Python-Sprache bereits gründlich missbrauche, indem ich 'sys.modules [__ name__] durch eine benutzerdefinierte Klasse ersetze, die' __getattr__' implementiert, was bedeutet, dass meine Funktionen nicht direkt aufgerufen werden. Vielen Dank, dies ist in vielen Fällen eine gute Option, wie es für die meisten Benutzer ist. –

+2

Die von ['locals()'] (https://docs.python.org/3/library/functions.html#locals) zurückgegebene Ansicht sollte nicht geändert werden. – poke

+0

Das Ändern von Locals wird nicht unterstützt. – user2357112

3

Haftungsausschluss: Diese Lösung kann nur globale Variablen ändern, also ist es nicht perfekt. Siehe Is it good practice to use import __main__? für Vor- und Nachteile.

In foo.py:

import __main__ 

def set_a_to_three(): 
    __main__.a = 3 

__main__ ist ein Name auf den aktuellen Top-Level-Programm gegeben. Wenn Sie beispielsweise foo von einem Modul bar importieren, kann bar mit dem Namen __main__ referenziert werden. Wenn Sie foo von bar importieren, das ursprünglich von einem Modul qux importiert wurde, dann ist qux__main__, usw.

bar -> foo 
bar is __main__ 

qux -> bar -> foo 
qux is __main__ 

Wenn Sie tatsächlich Variablen in bar zu ändern, anstatt qux, zum Beispiel weil qux Unit-Tests enthält, können Sie so etwas wie diese verwenden:

import sys 
import __main__ 
if "bar" in sys.modules: 
    __main__ = sys.modules["bar"] 
2

Es ist einfach, in der __main__ zu setzen, weil wir seinen Namen kennen.

#foo.py 
import sys 

def set_a_to_three(): 
    sys.modules['__main__'].a = 3 
+0

Könnte das mit einem 'setattr' gemacht werden, wenn ich seinen Namen nicht kannte? –

+0

Ich sagte den Namen des Moduls. –

+1

Rechts. Mein Fehler. Es tut uns leid ;) –