2016-07-03 38 views
5

Vorrei una funzione nel mio modulo per poter accedere e modificare lo spazio dei nomi locale dello script che lo sta importando. Ciò abiliterebbe funzioni come questa:Modifica lo spazio dei nomi dello script di importazione in Python

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

E 'possibile in Python?

+0

Che dire di solo 'a = 3'? –

+1

È solo un esempio stupido. Farò cose più complesse che devono venire da "pippo" e essere assegnate da "pippo". So però come funziona l'assegnazione di Python normalmente. –

+0

Non possibile, no. Si potrebbe fare in modo che una funzione modifichi i globali, ma può solo accedere ai globali del modulo in cui è definito, non da quello da cui proviene. – poke

risposta

3

Una risposta è stato generosamente fornito da @omz in una squadra Slack:

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

Ciò fornisce il vantaggio rispetto alla soluzione di __main__ che funziona a più livelli di importazioni, per esempio se a importazioni b che importa il mio modulo , foo, foo possibile modificare b 's globali, non solo a' s (credo)

Tuttavia, se ho capito bene, modificando lo spazio dei nomi locale è più complicato. Come indicato da altri:

Si interrompe quando si chiama quella funzione da un'altra funzione. Quindi il prossimo spazio dei nomi in cima allo stack è un falso dict di gente del posto, a cui non puoi scrivere. Bene, puoi, ma le scritture vengono ignorate.

Se c'è una soluzione più affidabile, sarebbe molto apprezzata.

3

Per consentire al modulo di accedere allo spazio dei nomi dello script, è necessario passare lo spazio dei nomi alla funzione. Per esempio:

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

L'interno del codice sarebbe simile a questa:

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

Nota a margine: in questo caso, il dizionario globals non è necessaria, ma nei casi in cui è necessario modificare variabile globale, sarà necessario utilizzare il dizionario globale. L'ho incluso qui per questo motivo per rendere estensibile la funzione.

Tuttavia, sarebbe più semplice impostare a = 3 come ha detto Eli Sadoff. Non è una buona pratica passare lo spazio dei nomi ad altri programmi.

+0

Questa è una risposta decente in molti casi. La mia situazione è * leggermente * più complicata perché sto già abusando del linguaggio Python sostituendo 'sys.modules [__ name__' con una classe personalizzata che implementa' __getattr__', il che significa che le mie funzioni non vengono chiamate direttamente. Grazie mille, questa è una buona opzione in molti casi, come lo è per la maggior parte degli utenti. –

+2

La vista restituita da ['locals()'] (https://docs.python.org/3/library/functions.html#locals) non deve essere modificata. – poke

+0

La modifica dei locali non è supportata. – user2357112

3

Disclaimer: questa soluzione può modificare solo le variabili globali, quindi non è perfetta. Vedi Is it good practice to use import __main__? per pro e contro.

In foo.py:

import __main__ 

def set_a_to_three(): 
    __main__.a = 3 

__main__ è un nome dato al programma di alto livello corrente. Ad esempio, se si importa foo da un modulo bar, è possibile fare riferimento a bar con il nome __main__. Se importate da bar, che è stato inizialmente importato da un modulo qux, quindi qux è __main__, ecc.

bar -> foo 
bar is __main__ 

qux -> bar -> foo 
qux is __main__ 

Se si vuole realmente modificare le variabili in bar piuttosto che qux, ad esempio perché qux contiene test di unità, è possibile utilizzare qualcosa di simile:

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

E 'facile da impostare nel __main__, perché sappiamo che è il nome.

#foo.py 
import sys 

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

Questo potrebbe essere fatto con un 'setattr' se non conoscevo il suo nome? –

+0

Ho detto il nome del modulo. –

+1

Giusto. Colpa mia. Scusate ;) –