2009-07-17 12 views
5

Come posso modificare lo spazio dei nomi locale di una funzione in python? So che locals() restituisce lo spazio dei nomi locale della funzione quando viene chiamato al suo interno, ma voglio fare qualcosa di simile (ho una ragione per cui voglio farlo dove g non è accessibile a f, ma è più veloce da dare un banale, stupido esempio per illustrare il problema):Come modificare lo spazio dei nomi locale in python

def g(): 
    pass 

def f(): 
    g() 

f.add_to_locals({'g':g}) 
+0

Qual è il tuo obiettivo finale utilizzando questa strana tecnica? Perché non puoi semplicemente definire g() prima di f()? –

+0

Ho provato a pasticciare con lo spazio dei nomi di una funzione in precedenza in fase di esecuzione (per test/mocking) e fallito ... solo FYI. –

+0

cosa diamine è "add_to_locals"? Non sembra esistere per me. 'AttributeError: l'oggetto 'function' non ha attributo 'add_to_locals'' –

risposta

3

Poiché la funzione non viene richiamata, non ha stack frame "locale", ancora. La soluzione più semplice è quella di utilizzare un contesto globale:

handler = None 
def f(): 
    handler() 

def g(): pass 

handler = g 

Oppure si potrebbe impostare g sul oggetto funzione:

f.g = g 

Ma io non sono sicuro di come si può ottenere l'oggetto funzione dall'interno la funzione stessa. Se fosse un metodo, useresti self.

+0

Se * veramente * voleva utilizzare l'oggetto funzione all'interno della funzione, è possibile utilizzare ispezionare: import ispezionare importazione pprint FN DEF(): frame = inspect.currentframe() frame.f_globals stampa [cornice .f_code.co_name] .g fn.g = 'hello' fn() – Ivo

+0

Questo è rotto per funzioni di classe, funzioni/chiusure annidate, e lambda, e qualsiasi cosa decorata - restituirà la funzione di avvolgimento del decoratore, non la funzione attuale. –

+0

cosa succede se 'f' è un metodo di classe/istanza? –

1

Una funzione che non è in esecuzione non ha alcuna gente del posto; il contesto locale viene creato quando si esegue la funzione e distrutto quando esce, quindi non c'è "spazio dei nomi locale" da modificare al di fuori della funzione.

Si può fare qualcosa di simile, però:

def f(): 
    g = [1] 
    def func(): 
     print g[0] 
    return func, g 

f, val = f() 
f() 
val[0] = 2 
f() 

Questo utilizza una matrice per simulare un punto di riferimento.

+2

Questo sito dovrebbe davvero richiedere spiegazioni per -1 voti, quindi le persone non possono votare in modo anonimo le risposte corrette senza una ragione. –

2

Penso che potresti risolvere il problema affrontandolo da un punto completamente diverso.
Le funzioni sono oggetto, con i loro dizionari; Pertanto, è possibile aggiungere g di f, e usarlo:

def g(): 
    print "g" 

def f(): 
    f.g() 

f.g = g 
+0

Soluzione molto elegante. +1 – Josip

+0

Funziona solo per funzioni globali; non c'è modo di accedere in modo affidabile da dentro f. –

+0

Potresti espanderlo? Le funzioni Python sono oggetti, e non ci sono differenze se locali o globali - a meno che tu non intenda _methods_ ma questa è una storia completamente diversa, e non ciò che l'OP chiedeva. –

3

Perché non basta aggiungere un argomento per f() e passare un riferimento a g()?

def g(): 
    pass 

def f(func): 
    func() 

f(g) 
2

Presumo che si vuole fare questo, perché la funzione f è definita non da te, ma da qualche altro modulo. Quindi vuoi cambiare il modo in cui f() funziona. In particolare, si desidera modificare ciò che viene chiamato quando si chiama g.

Così io suggerisco questo:

import thirdpartypackage 

def mynewg(): 
    pass 

thirdpartypackage.g = mynewg 

Questo cambierà la g globale per il thirdpartypackage modulo. Quindi, quando viene chiamato thirdpartypackage.f(), chiamerà mynewg() anziché g().

Se questo non lo risolve, forse g() è in realtà importato da withing f(), o qualcosa. Allora la soluzione è questa:

import thirdpartypackage 

def mynewg(): 
    pass 

deg mynewf(): 
    mynewg() 

thirdpartypackage.f = mynewf 

Cioè, si ignora f() completamente con una versione modificata che fa quello che si vuole.

8

Hai un paio di opzioni. Innanzitutto, nota che g nell'esempio non è in realtà un locale per la funzione (cioè non è assegnato all'interno di esso), è un globale (cioè non è stato assegnato a una variabile locale). Ciò significa che verrà esaminato nel modulo in cui è definita la funzione. Questa è una fortuna, poiché non c'è modo di alterare i locali esternamente (a meno di applicare patch al bytecode), poiché vengono assegnati quando la funzione viene eseguita, non prima.

Un'opzione è semplicemente quella di iniettare la funzione nello spazio dei nomi del modulo della funzione. Questo funzionerà, ma interesserà ogni funzione in quel modulo che accede alla variabile, piuttosto che solo una funzione.

Per influenzare solo una funzione, è necessario invece puntare su func_globals da qualche altra parte. Purtroppo, questa è una proprietà di sola lettura, ma si può fare ciò che si vuole ricreando la funzione con lo stesso corpo, ma un diverso namespace globale:

import new 
f = new.function(f.func_code, {'g': my_g_function}, f.func_name, f.func_defaults, f.func_closure) 

f sarà ora indentico, tranne che sarà per i globali nel dict fornito. Nota che questo riassembla l'intero spazio dei nomi globale - se ci sono variabili che f fa, assicurati di fornire anche loro. Questo è anche abbastanza hacky e potrebbe non funzionare su versioni di python diverse da cpython.

+1

Non so se questo è un codice "buono", ma era esattamente quello che volevo. Sto convertendo un codice base in un altro e volevo essere in grado di simulare un costrutto linguistico dall'altra lingua. L'altra lingua può comprimere il codice e aggiungere o rimuovere quel codice dallo spazio dei nomi. È utile per ignorare le diverse funzioni. Quindi manipola lo spazio dei nomi in una pila come la moda. Questa è la cosa più vicina che riesco a trovare che mi consenta di farlo in modo efficiente. Grazie – Demolishun

+0

cos'è questa libreria 'nuova'? –

+0

cosa non va con 'exec' o' locals(). Update (d) '? –

0

Questo sembra funzionare

def add_to_locals(l): 
    l['newlocal'] = 1 

add_to_locals(locals()) 
assert newlocal 
+0

Questo non funziona all'interno di una funzione. Non aggiunge il valore allo spazio dei nomi. – Demolishun

Problemi correlati