假设
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__
属性。这怎么办?
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)
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 所建议的。我不确定此方法的限制和注意事项(多线程很可能是一个问题),但我打算在我的情况下使用它。
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)
import inspect
frame = inspect.stack()[-1]
print(frame.filename)
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
,至少你不会遇到异常并且可以仍然得到一个合理的返回值)。