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.