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

问题描述 投票:0回答:5

假设

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

 设置为调用函数模块(即“myapp.foo”)的 
__name__
 属性。这怎么办?

python stack-trace introspection
5个回答
172
投票
检查检查模块:

inspect.stack()

 将返回堆栈信息。

在函数内部,

inspect.stack()[1]

将返回调用者的堆栈。  从那里,您可以获得有关调用者的函数名称、模块等的更多信息。

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

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

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

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

编辑:我认为这里有一些代码可以实现你想要的功能:

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

31
投票
面对类似的问题,我发现 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 所建议的。我不确定此方法的限制和注意事项(多线程很可能是一个问题),但我打算在我的情况下使用它。


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

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)
    

5
投票
对于我来说,下面的行足以获取来电者的姓名。

import inspect frame = inspect.stack()[-1] print(frame.filename)
    

0
投票
这是一个使用

inspect

 的版本,与上面的答案类似,但不是获取整个堆栈,而是只遍历我们感兴趣的帧(默认情况下是前 2 个),并处理可能出现的情况  None
,因此您不会得到 
AttributeError
,而是返回默认值。您还可以配置深度(如果深度为 1,则意味着返回直接调用者 - 相当于 
inspect.stack[1]
 - 等等)。

import inspect def caller_module_name(depth: int = 1, default: str | None = '__main__') -> str | None: frame = inspect.currentframe() if not frame: return default # Go back up the stack to the caller (or caller's caller, etc.) # (add 1 to depth to account for the this function itself) for _ in range(depth + 1): if not (frame := frame.f_back): return default if module := inspect.getmodule(frame): return module.__name__ return default
我不确定默认值的最佳选择是什么,

"__main__"

__name__
或只是
None
都可能是合适的。下面提到的打字模块中的内部函数使用硬编码字符串
"__main__"
作为默认值,所以我做了同样的事情,但是作为默认值有意义的可能取决于应用程序以及它应该在哪里/如何运行.

看看 CPython 源代码内部是如何完成的可能会很有趣,例如

在打字模块中。然而,这是使用内部 API,并且仅保证在 CPython 上工作,并且没有向后兼容的保证。所以最好使用公共 API 来编写它(在 inspect

 模块中)。 (显然,正如检查模块文档中所警告的那样,它仍然无法在没有堆栈帧支持的 Python 解释器上工作,但是通过使用 
inspect
 模块并处理 
None
,至少你不会遇到异常并且可以仍然得到一个合理的返回值)。

© www.soinside.com 2019 - 2024. All rights reserved.