在Python中获取调用函数模块的__name__

Rei*_*ica 87 python introspection stack-trace

假设myapp/foo.py包含:

def info(msg):
    caller_name = ????
    print '[%s] %s' % (caller_name, msg)
Run Code Online (Sandbox Code Playgroud)

myapp/bar.py包含:

import foo
foo.info('Hello') # => [myapp.bar] Hello
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我希望caller_name将其设置为__name__调用函数模块的属性(即'myapp.foo').如何才能做到这一点?

ars*_*ars 112

检查检查模块:

inspect.stack() 将返回堆栈信息.

在函数内部,inspect.stack()[1]将返回调用者的堆栈.从那里,您可以获得有关调用者的函数名称,模块等的更多信息.

有关详细信息,请参阅文档:

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

此外,Doug Hellmann在他的PyMOTW系列中有一个很好的检查模块的文章:

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

编辑:这里有一些代码可以做你想要的,我想:

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

  • 请注意,这将与导入钩子奇怪地相互作用,不会对ironpython起作用,并且可能在jython上以令人惊讶的方式表现.如果你能像这样避免魔法,那就最好了. (6认同)
  • 请注意,如果调用函数被装饰(@ ...),则需要为真实调用者访问`inspect.stack()[2]`. (6认同)
  • 那么如何使用“inspect”模块获取该模块的“__name__”属性呢?例如,如何在上面的示例中返回“myapp.foo”(而不是“myapp/foo.py”)?在发布 SO 之前我已经尝试使用检查模块。 (2认同)
  • 另请注意,保持对堆栈帧的引用可能会阻止Python的GC正常工作.请参阅此处的警告:http://docs.python.org/library/inspect.html#the-interpreter-stack (2认同)

Lou*_* LC 19

面对类似的问题,我发现sys模块中的sys._current_frames()包含有用的信息,可以帮助您,而不需要导入检查,至少在特定的用例中.

>>> sys._current_frames()
{4052: <frame object at 0x03200C98>}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用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__'
Run Code Online (Sandbox Code Playgroud)

对于文件名,您也可以使用f.f_back.f_code.co_filename,如上面的Mark Roddy所建议的那样.我不确定这种方法的限制和警告(多线程很可能是一个问题),但我打算在我的情况下使用它.

  • 我认为通过[`sys._getframe(1)`](https://docs.python.org/3/library/sys.html#sys._getframe)获取前一帧更容易,而不是调用`sys. _current_frames()`(btw返回每个线程的帧映射). (7认同)
  • 注意:在使用pyinstaller编译为exe后,inspect.stack代码为FAILS,但使用sys._current_frames WORKS FINE ...所以这是我的首选技术. (2认同)

Mar*_*ddy 7

我不建议这样做,但您可以通过以下方法来实现您的目标:

def caller_name():
    frame=inspect.currentframe()
    frame=frame.f_back.f_back
    code=frame.f_code
    return code.co_filename
Run Code Online (Sandbox Code Playgroud)

然后更新您现有的方法,如下所示:

def info(msg):
    caller = caller_name()
    print '[%s] %s' % (caller, msg)
Run Code Online (Sandbox Code Playgroud)

  • 文件名与“__name__”不同 (13认同)