Come posso accedere al modulo di esecuzione corrente o al nome della classe in Python?
Domanda
Vorrei essere in grado di recuperare dinamicamente il modulo di esecuzione corrente o il nome della classe da un modulo importato. Ecco un po 'di codice:
foo.py:
def f():
print __name__
bar.py:
from foo import f
def b(): f()
Questo ovviamente non funziona poiché __name__
è il nome del modulo che contiene la funzione. Quello a cui vorrei accedere all'interno del modulo foo
è il nome dell'attuale modulo in esecuzione che utilizza foo
. Quindi nel caso sopra sarebbe bar
ma se qualsiasi altro modulo importasse foo
vorrei che foo
potesse accedere dinamicamente al nome di quello modulo.
Modifica: Il modulo inspect
sembra abbastanza promettente ma non è esattamente quello che stavo cercando. Quello che speravo fosse una sorta di variabile globale o di livello ambientale a cui potessi accedere che contenesse il nome del modulo di esecuzione corrente. Non che non sia disposto a attraversare lo stack per trovare quelle informazioni - ho solo pensato che Python potrebbe aver già esposto quei dati.
Modifica: Ecco come sto cercando di utilizzare questo. Ho due diverse applicazioni Django che devono entrambe registrare 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 qualsiasi momento vorrei poter importare il mio modulo logger e chiamare la funzione di registro che scrive la stringa di registro su file. Tuttavia, ciò che vorrei fare è creare una directory in app_logs
che sia il nome dell'applicazione corrente (" AppOne " o " AppTwo ") in modo che i file di registro di ciascuna applicazione andranno nei rispettivi directory di log.
Per fare questo ho pensato che il modo migliore sarebbe che il modulo logger avesse 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 della creazione la directory di registrazione dell'applicazione se non esiste ancora.
Soluzione
Dal commento, non dalla domanda.
Sono semplicemente curioso di vedere se ciò 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 non è chiara. La vera domanda è "qual è il modo migliore per far conoscere al mio modulo di registrazione il nome del client?" o qualcosa del genere.
La risposta è " Accettala come parametro. " Non scherzare con l'ispezione o la ricerca di misteriosi globi o altri trucchi.
Segui semplicemente il modello di progettazione di logging.getLogger () e usa logger con nome esplicito. Un linguaggio comune è il seguente
logger= logging.getLogger( __name__ )
Gestisce perfettamente quasi tutti i nomi dei log.
Altri suggerimenti
Questo dovrebbe funzionare per fare riferimento al modulo corrente:
import sys
sys.modules[__name__]
Il modulo "attualmente in esecuzione" chiaramente è foo, poiché questo è ciò che contiene la funzione attualmente in esecuzione - penso che una migliore descrizione di ciò che vuoi sia il modulo del chiamante immediato di foo (che potrebbe essere esso stesso se stai chiamando af () da una funzione in foo chiamata da una funzione nella barra. La misura in cui vuoi salire dipende da cosa desideri.
In ogni caso, supponendo che tu voglia il chiamante immediato, puoi ottenerlo salendo nello stack di chiamate. Ciò può essere ottenuto chiamando sys._getframe
, con il numero appropriato di livelli da percorrere.
def f():
caller = sys._getframe(1) # Obtain calling frame
print "Called from module", caller.f_globals['__name__']
[Modifica] : in realtà, utilizzando inspect Il modulo come suggerito sopra è probabilmente un modo più pulito di ottenere il frame dello stack. Il codice equivalente è:
def f():
caller = inspect.currentframe().f_back
print "Called from module", caller.f_globals['__name__']
(sys._getframe è documentato come per uso interno - il modulo inspect è un'API più affidabile)
__file__
è il percorso del modulo corrente in cui viene effettuata la chiamata.
Per ottenere un riferimento a " __ main __ " modulo in un altro:
import sys
sys.modules['__main__']
Per ottenere quindi il percorso del file del modulo, che include il suo nome:
sys.modules['__main__'].__file__
Se all'interno di " __ main __ " modulo, basta usare: __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__
Non credo sia possibile poiché non rientra nell'ambito di foo
. foo sarà a conoscenza del suo ambito interno poiché potrebbe essere chiamato da innumerevoli altri moduli e applicazioni.
L'uso del __file__
da solo fornisce un percorso relativo per il modulo principale e un percorso assoluto per i moduli importati. Consapevole di ciò, possiamo ottenere costantemente il file del modulo 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 vuoi eliminare '.py' dalla fine, puoi farlo facilmente. (Ma non dimenticare che puoi eseguire un '.pyc' invece.)
È passato un po 'di tempo da quando ho fatto Python, ma credo che tu possa avere accesso ai globi e ai locali di un chiamante tramite traceback .
Se vuoi solo il nome del file:
file_name = __file__.split("/")[len(__file__.split("/"))-1]
Per ottenere il modulo di file corrente, contenente la cartella, ecco cosa ha funzionato per me:
import os
parts = os.path.splitext(__name__)
module_name = parts[len(parts) - 2]