2009-07-08 8 views
64

Angenommen myapp/foo.py enthält:Get __name__ von in Python-Funktion des Moduls Aufruf

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

Und myapp/bar.py enthält:

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

Ich möchte caller_name zum __name__ Attribut des anrufenden Funktionen Modul eingestellt werden (Das ist 'myapp.foo') in diesem Fall. Wie kann das gemacht werden?

+0

Angenommen, ein anderes Einstiegspunktskript ruft bar.py .. auf und somit kann 'caller_name' nicht' __main__' sein. –

+1

Sind Sie sicher, dass Ihr Beispielcode korrekt ist? Sollte der Körper von myapp/bar.py nicht sein: import foo; foo.info ('Hallo') # => [myapp.bar] Hallo –

+0

Richtig, ich habe den Code repariert. –

Antwort

91

Überprüfen Sie das Modul überprüfen out:

inspect.stack() die Stack-Informationen zurück.

Innerhalb einer Funktion gibt inspect.stack()[1] den Stapel des Aufrufers zurück. eine nette Zuschreibung des Moduls prüfen

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

Auch Doug Hellmann hat in: Von dort können Sie weitere Informationen über die Anrufer-Funktion Name, Modul usw.

Lesen Sie die Dokumentation für Details erhalten seine PyMOTW Serie:

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

EDIT: Hier ist ein Code, der tut, was Sie wollen, denke ich:

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

Wie erhalten Sie also das '__name__' Attribut dieses Moduls mit dem' inspect' Modul? Zum Beispiel, wie bekomme ich 'myapp.foo' (nicht' myapp/foo.py') in meinem obigen Beispiel zurück? Ich habe bereits versucht, das inspect-Modul zu verwenden, bevor ich bei SO posten konnte. –

+0

Nur die Antwort aktualisiert. Funktioniert das an deinem Ende? – ars

+6

Beachten Sie, dass dies merkwürdig mit Import-Hooks interagiert, auf ironpython nicht funktioniert und sich auf jython in überraschender Weise verhalten kann.Es ist am besten, wenn du Magie so vermeiden kannst. – Glyph

2

Ich empfehle dies nicht tun, aber Sie können Ihr Ziel mit dem folgenden Verfahren erreichen:

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

Dann aktualisieren Sie Ihre bestehende Methode wie folgt:

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

Dateiname ist nicht das selbe wie '__name__' –

16

mit einem ähnlichen Konfrontiert Problem, ich habe festgestellt, dass sys._current_frames() aus dem sys-Modul enthält interessante Informationen, die Ihnen helfen können, ohne die Notwendigkeit zu importieren, zumindest in bestimmten Anwendungsfällen.

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

Sie können "move up" mit f_back dann:

>>> f = 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__' 

Für den Dateinamen auch f.f_back.f_code.co_filename verwenden können, wie oben von Mark Roddy vorgeschlagen. Ich bin mir nicht sicher über die Grenzen und Vorbehalte dieser Methode (mehrere Threads werden wahrscheinlich ein Problem sein), aber ich beabsichtige, es in meinem Fall zu verwenden.

+1

hinweis: der inspect.stack code FAILS nach kompilieren zu exe mit pyinstaller, aber mit sys._current_frames WORKS FINE ... so ist dies die bevorzugte Technik für mich. – panofish

+5

Ich denke, es ist einfacher, den vorherigen Frame mit ['sys._getframe (1)'] (https://docs.python.org/3/library/sys.html#sys._getframe) zu erhalten, anstatt 'sys aufzurufen. _current_frames() '(btw gibt eine Frame-Zuordnung für jeden Thread zurück.) – hooblei

+0

Vielen Dank, hooblei, ich habe es noch nicht getestet, aber es scheint sehr nützlich für Multithread-Situationen. –