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


96

假设myapp/foo.py包含:

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

myapp/bar.py包含:

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

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


假设其他入口点脚本调用bar.py ..因此caller_name不能为__main__
Sridhar Ratnakumar 09年

Answers:


123

检出检查模块:

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)

1
那么如何__name__使用该inspect模块获取该模块的属性?例如,如何在以上示例中返回myapp.foo(不是myapp/foo.py)?在SO上发布之前,我已经尝试过使用inspect模块。
Sridhar Ratnakumar,2009年

6
请注意,这将与导入挂钩发生奇怪的交互,在ironpython上不起作用,并且在jython上可能表现出令人惊讶的方式。最好是避免这种魔术。
雕文

2
另请注意,保持对堆栈框架的引用可能会阻止Python的GC正常工作。请在此处查看警告:docs.python.org/library/inspect.html#the-interpreter-stack
Kamil Kisiel 2010年

6
请注意,如果调用方功能已修饰(@ ...),则需要访问inspect.stack()[2]真正的调用方。
阿米尔·阿里·阿克巴里

另请注意,当您使用pyinstaller将python代码编译为exe时,此逻辑无法正常工作。
panofish

19

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

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

然后,您可以使用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__'

对于文件名,您还可以使用f.f_back.f_code.co_filename,如上文Mark Roddy所建议。我不确定此方法的局限性和注意事项(很可能会出现多个线程),但是我打算在自己的情况下使用它。


2
注意:使用pyinstaller编译为exe后,inspect.stack代码将失败,但是使用sys._current_frames则可能是FINES ...所以这是我的首选技术。
panofish

7
我认为通过sys._getframe(1)而不是调用sys._current_frames()(btw返回每个线程的帧映射)来获取上一帧更容易。
hooblei 2015年

谢谢hooblei,我尚未对其进行测试,但对于多线程情况似乎非常有用。
路易LC

我更喜欢使用inspect.currentframe()而不是sys._current_frames().values()[0]
阿兰·菲

3

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

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

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

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

7
文件名与__name__
Sridhar Ratnakumar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.