在 Python 中获取调用函数模块的 __name__
- 2025-03-13 09:09:00
- admin 原创
- 83
问题描述:
假设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”)的属性。如何实现?
解决方案 1:
检查检查模块:
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)
解决方案 2:
面对类似的问题,我发现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 所建议的。我不确定此方法的限制和注意事项(多线程很可能是个问题),但我打算在我的案例中使用它。
解决方案 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)
解决方案 4:
对于我来说,以下这句话就足以获得来电者的姓名。
import inspect
frame = inspect.stack()[-1]
print(frame.filename)
解决方案 5:
这是一个使用 的版本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 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 源代码中是如何内部完成的可能会很有趣,例如这里的 typing module。但是,这是使用内部 API 的,并且只能保证在 CPython 上工作,并且没有向后兼容的保证。因此,最好使用公共 API(在inspect
模块中)编写它。(显然,它仍然无法在没有堆栈框架支持的 Python 解释器上运行,正如检查模块文档中警告的那样,但通过使用inspect
模块和处理None
,至少您不会遇到异常,并且仍然可以获得合理的返回值)。
解决方案 6:
调用inspect.getmodule()
框架对象枚举中的模块对象,sys.modules
并将框架对象对应的代码对象的文件路径与模块对象的文件路径(module.__file__
针对每个模块)进行比较。
以下解决方案可避免此类开销。
import sys
def info(msg):
caller_name = sys._getframe(1).f_globals['__name__']
...
扫码咨询,免费领取项目管理大礼包!