Ottieni __name__ della chiamata del modulo della funzione in Python


96

Supponiamo che myapp/foo.pycontenga:

def info(msg):
    caller_name = ????
    print '[%s] %s' % (caller_name, msg)

E myapp/bar.pycontiene:

import foo
foo.info('Hello') # => [myapp.bar] Hello

Voglio caller_nameessere impostato __name__sull'attributo del modulo delle funzioni chiamanti (che è 'miaapp.foo') in questo caso. Come si può fare?


Supponiamo che qualche altro script del punto di ingresso richiami bar.py .. e quindi caller_namenon possa essere__main__
Sridhar Ratnakumar

Risposte:


123

Controlla il modulo di ispezione:

inspect.stack() restituirà le informazioni sullo stack.

All'interno di una funzione, inspect.stack()[1]restituirà lo stack del chiamante. Da lì, puoi ottenere maggiori informazioni sul nome della funzione del chiamante, sul modulo, ecc.

Vedere i documenti per i dettagli:

http://docs.python.org/library/inspect.html

Inoltre, Doug Hellmann ha una bella descrizione del modulo di ispezione nella sua serie PyMOTW:

http://pymotw.com/2/inspect/index.html#module-inspect

EDIT: ecco un po 'di codice che fa quello che vuoi, penso:

def info(msg):
    frm = inspect.stack()[1]
    mod = inspect.getmodule(frm[0])
    print '[%s] %s' % (mod.__name__, msg)

1
Quindi come si ottiene l' __name__attributo di questo modulo utilizzando il inspectmodulo? Ad esempio, come posso tornare myapp.foo(non myapp/foo.py) nel mio esempio precedente? Ho già provato a utilizzare il modulo di ispezione prima di pubblicare su SO.
Sridhar Ratnakumar

6
Tieni presente che questo interagirà in modo strano con gli hook di importazione, non funzionerà su ironpython e potrebbe comportarsi in modi sorprendenti su jython. È meglio se puoi evitare magie come questa.
Glifo

2
Si noti inoltre che mantenere un riferimento a uno stack frame può impedire il corretto funzionamento del GC di Python. Vedi l'avviso qui: docs.python.org/library/inspect.html#the-interpreter-stack
Kamil Kisiel

6
Nota che se la funzione chiamante è decorata (@ ...), devi accedere inspect.stack()[2]per il chiamante reale.
Amir Ali Akbari

Si noti inoltre che questa logica non funziona correttamente quando si compila il codice python in un exe utilizzando pyinstaller.
panofish

19

Di fronte a un problema simile, ho scoperto che sys._current_frames () dal modulo sys contiene informazioni interessanti che possono aiutarti, senza la necessità di importare inspect, almeno in casi d'uso specifici.

>>> sys._current_frames()
{4052: <frame object at 0x03200C98>}

Puoi quindi "spostarti verso l'alto" usando f_back:

>>> f = sys._current_frames().values()[0]
>>> # for python3: f = list(sys._current_frames().values())[0]

>>> print f.f_back.f_globals['__file__']
'/base/data/home/apps/apricot/1.6456165165151/caller.py'

>>> print f.f_back.f_globals['__name__']
'__main__'

Per il nome del file puoi anche usare f.f_back.f_code.co_filename, come suggerito da Mark Roddy sopra. Non sono sicuro dei limiti e delle avvertenze di questo metodo (più thread molto probabilmente saranno un problema) ma intendo utilizzarlo nel mio caso.


2
nota: il codice inspect.stack NON FAIL dopo la compilazione in exe usando pyinstaller, ma usando sys._current_frames FUNZIONA BENE ... quindi questa è la tecnica preferita per me.
panofish

7
Penso che sia più facile ottenere il frame precedente sys._getframe(1), invece di chiamare sys._current_frames()(btw che restituisce una mappatura del frame per ogni thread).
hooblei

Grazie hooblei, non l'ho ancora testato ma sembra molto utile per le situazioni multi-thread.
Louis LC

Preferisco usare inspect.currentframe()invece di sys._current_frames().values()[0].
Aran-Fey

3

Non consiglio di farlo, ma puoi raggiungere il tuo obiettivo con il seguente metodo:

def caller_name():
    frame=inspect.currentframe()
    frame=frame.f_back.f_back
    code=frame.f_code
    return code.co_filename

Quindi aggiorna il tuo metodo esistente come segue:

def info(msg):
    caller = caller_name()
    print '[%s] %s' % (caller, msg)

7
Il nome del file non è lo stesso di__name__
Sridhar Ratnakumar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.