2009-03-02 16 views
22

Mi piacerebbe essere in grado di recuperare in modo dinamico il modulo di esecuzione o il nome della classe corrente da un modulo importato. Ecco il codice:Come posso accedere al modulo di esecuzione o al nome della classe corrente in Python?

foo.py:

def f(): 
    print __name__ 

bar.py:

from foo import f 

def b(): f() 

Questo, ovviamente, non funziona come __name__ è il nome del modulo che contiene la funzione. Quello che mi piacerebbe avere accesso all'interno del modulo foo è il nome del modulo in esecuzione corrente che utilizza foo. Quindi nel caso sopra sarebbe bar ma se qualsiasi altro modulo importato foo mi piacerebbe che foo abbia accesso dinamico al nome di quel modulo.

Modifica: Il modulo inspect sembra abbastanza promettente ma non è esattamente quello che stavo cercando. Quello che speravo era una sorta di variabile globale o di livello ambientale a cui potevo accedere che avrebbe contenuto il nome del modulo di esecuzione corrente. Non che io non voglia attraversare lo stack per trovare quell'informazione - ho solo pensato che Python potesse aver già esposto quei dati.

Modifica: Ecco come sto cercando di utilizzare questo. Ho due diverse applicazioni Django che hanno entrambi bisogno di registrare gli errori nel file. Diciamo che sono chiamati "AppOne" e "AppTwo". Ho anche un posto in cui vorrei registrare questi file: "/home/hare/app_logs". In ogni applicazione in un dato punto mi piacerebbe essere in grado di importare il mio modulo logger e chiamare la funzione di registro che scrive la stringa di registro sul file. Tuttavia, ciò che vorrei fare è creare una directory sotto app_logs che è il nome dell'applicazione corrente ("AppOne" o "AppTwo") in modo che i file di registro di ogni applicazione andranno nelle rispettive directory di registrazione.

Per fare questo ho pensato che il modo migliore sarebbe stato per il modulo logger di avere accesso a una sorta di variabile globale che denota il nome dell'applicazione corrente in quanto è responsabile della conoscenza della posizione della directory di registrazione principale e creando la directory di registrazione dell'applicazione se non esiste ancora.

+0

Perché si vuole rompere l'attribuzione di responsabilità come questo ? –

+1

Sto usando questo all'interno di un modulo di registrazione - Voglio essere in grado di dire quale modulo esterno sta tentando di accedere senza che il chiamante debba passare una chiave di qualche tipo. –

+0

Stai cercando il modulo che ha avviato l'esecuzione del tuo programma (cioè il cui __name__ == '__main__'? O letteralmente il modulo che ha chiamato foo.f(), che può essere diverso ogni volta che foo.f() è chiamato? –

risposta

40

Dal commento - non la domanda.

Sono semplicemente curioso di vedere se quello che sto cercando di fare è possibile.

La risposta a "è possibile" è sempre "sì". Sempre. A meno che la tua domanda non riguardi il viaggio nel tempo, l'antigravità o il moto perpetuo.

Poiché la risposta è sempre "sì", la tua domanda è mal formata. La vera domanda è "qual è un buon modo per avere il mio modulo di registrazione sapere il nome del cliente?" o qualcosa di simile.

La risposta è "Accettalo come parametro". Non scherzare con l'ispezione o la ricerca di misteriosi globali o altri trucchi.

Basta seguire lo schema di progettazione di logging.getLogger() e utilizzare logger con nome esplicito. Un idioma comune è il seguente

logger= logging.getLogger(__name__) 

Che gestisce quasi tutti i nomi di registro perfettamente.

+1

Penso che tu abbia ragione nel dire che starò meglio adattando il mio approccio - grazie! –

+0

Uno dei miei professori di CS aveva una frase che ha imparato a usare quando lavorava nel settore e qualcuno ha chiesto, "Puoi fare X?" La sua risposta è stata: "Posso fare qualsiasi cosa con abbastanza tempo e denaro". –

+5

Anche se eseguo lo script invece di importarlo, '__name__' conterrà' __main__' invece del vero percorso dello script (come 'modA.submodB.pack1'). Esiste un valore che * sempre * restituisce il percorso del modulo indipendentemente da come viene chiamato? – estani

3

Non credo sia possibile poiché questo è fuori dall'ambito di foo. foo sarà a conoscenza del suo ambito interno poiché potrebbe essere chiamato da innumerevoli altri moduli e applicazioni.

11

Penso che quello che si desidera utilizzare sia il modulo inspect, per ispezionare lo stack di runtime di python. Dai un'occhiata a questo tutorial. Penso che fornisca un esempio quasi esatto di ciò che vuoi fare.

+0

+1 questa è una buona scoperta. –

+7

Il collegamento è interrotto. –

15

Il "modulo attualmente in esecuzione" è chiaramente foo, poiché è quello che contiene la funzione attualmente in esecuzione - Penso che una descrizione migliore di ciò che si desidera sia il modulo del chiamante immediato di foo (che potrebbe essere esso stesso foo se si sta chiamando af() da una funzione in foo chiamata da una funzione in bar. Quanto lontano vuoi salire dipende da cosa desideri.

In ogni caso, supponendo che si desideri il chiamante immediato, è possibile ottenere questo salendo lo stack delle chiamate, che può essere ottenuto chiamando il numero sys._getframe, con il numero adeguato di livelli da percorrere.

def f(): 
    caller = sys._getframe(1) # Obtain calling frame 
    print "Called from module", caller.f_globals['__name__'] 

[Edit]: In realtà, utilizzando il modulo inspect come suggerito sopra è probabilmente un modo pulito per ottenere stack frame. Il codice equivalente è:

def f(): 
    caller = inspect.currentframe().f_back 
    print "Called from module", caller.f_globals['__name__'] 

(sys._getframe è documentato come essere per uso interno - l'ispezionare modulo è un'API più affidabile)

1

E 'stato un po' che ho fatto python, ma io credo che sia possibile accedere ai globali e ai locali di un chiamante attraverso il suo traceback.

7

__file__ è il percorso del modulo corrente in cui è stata effettuata la chiamata.

24

Questo dovrebbe funzionare per fare riferimento il modulo corrente:

import sys 
sys.modules[__name__] 
1

per ottenere un riferimento al modulo "__main__" quando in un altro:

import sys 
sys.modules['__main__'] 

Per poi ottenere il percorso del file del modulo, che include il suo nome:

sys.modules['__main__'].__file__ 

Se nel modulo "__main__", utilizzare semplicemente: __file__

Per ottenere solo il nome del file dal percorso del file:

import os 
os.path.basename(file_path) 

per separare il nome del file dalla sua estensione:

file_name.split(".")[0] 

per ottenere il nome di un'istanza di classe:

instance.__class__.__name__ 

Per ottenere il nome di una classe:

class.__name__ 
2

L'utilizzo di __file__ da solo fornisce un percorso relativo per il modulo principale e un percorso assoluto per i moduli importati. Essendo consapevoli di questo possiamo ottenere il file del modulo costantemente in entrambi i modi con un piccolo aiuto dai nostri strumenti os.path.

Per il nome file utilizzare solo __file__.split(os.path.sep)[-1].

Per il percorso completo utilizzare os.path.abspath(__file__).

Demo:

/tmp $ cat f.py 
from pprint import pprint 
import os 
import sys 

pprint({ 
    'sys.modules[__name__]': sys.modules[__name__], 
    '__file__': __file__, 
    '__file__.split(os.path.sep)[-1]': __file__.split(os.path.sep)[-1], 
    'os.path.abspath(__file__)': os.path.abspath(__file__), 
}) 

/tmp $ cat i.py 
import f 

Risultati:

## on *Nix ## 

/tmp $ python3 f.py 
{'sys.modules[__name__]': <module '__main__' from 'f.py'>, 
'__file__': 'f.py', 
'__file__.split(os.path.sep)[-1]': 'f.py', 
'os.path.abspath(__file__)': '/tmp/f.py'} 

/tmp $ python3 i.py 
{'sys.modules[__name__]': <module 'f' from '/tmp/f.pyc'>, 
'__file__': '/tmp/f.pyc', 
'__file__.split(os.path.sep)[-1]': 'f.pyc', 
'os.path.abspath(__file__)': '/tmp/f.pyc'} 

## on Windows ## 

PS C:\tmp> python3.exe f.py 
{'sys.modules[__name__]': <module '__main__' from 'f.py'>, 
'__file__': 'f.py', 
'__file__.split(os.path.sep)[-1]': 'f.py', 
'os.path.abspath(__file__)': 'C:\\tools\\cygwin\\tmp\\f.py'} 

PS C:\tmp> python3.exe i.py 
{'sys.modules[__name__]': <module 'f' from 'C:\\tools\\cygwin\\tmp\\f.py'>, 
'__file__': 'C:\\tools\\cygwin\\tmp\\f.py', 
'__file__.split(os.path.sep)[-1]': 'f.py', 
'os.path.abspath(__file__)': 'C:\\tools\\cygwin\\tmp\\f.py'} 

Se si vuole mettere a nudo il '.py' fuori alla fine, si può fare facilmente. (Ma non dimenticate che si può eseguire un '.pyc' invece.)

+0

qualsiasi motivo per '__file __. Split (os.path.sep) [- 1]' invece di 'os.path.basename (__file __) '? – cowbert

1

Se si desidera solo il nome del file:

file_name = __file__.split("/")[len(__file__.split("/"))-1] 
Problemi correlati