如何在Python中获取当前模块属性的引用

159

我想在命令行中执行以下操作:

>>> import mymodule
>>> names = dir(mymodule)

如何在mymodule内部获取对所有定义的名称的引用?

类似于这样:

# mymodule.py
names = dir(__thismodule__)

请自动调用所有与Python中特定模式匹配的函数。 - ksridhar
3个回答

211

如之前提到的,globals会给你一个字典,而dir()会给你模块中定义的名称列表。我通常看到的做法是这样的:

import sys
dir(sys.modules[__name__])

2
我本来想添加一条评论,说明这对于“__main__”模块(也就是在终端上运行的模块)不起作用,因为它似乎没有列在sys.modules中 - 但实际上它确实有效 :) - markm
然而,从ipdb中似乎无法正常工作(在您的文件中插入“import ipdb; ipdb.set_trace()”)。 - gatoatigrado
12
太好了!这让我可以将当前模块的docstring用作使用信息 - sys.modules[__name__].__doc__ - george
并且为了变得超级hacky。operators.attrgetter('module.attribute')(sys.modules[__name__]) - 你知道的,如果你做那些人们告诉你不要做的疯狂事情,比如从字符串动态导入包,然后在不在类中的情况下对它们进行monkey patch... - casey
太好了!这让我可以在我的模块的init()函数中使用sys.modules[__name__].get_config = lambda: config来冻结结果,一旦配置更改(只是全局变量)没有意义。我在我的log_lib中使用了这个技巧,它已经改进了近10年。 - Bruno Bronosky
5
阅读 George 的评论的人们:sys.modules[__name__].__doc__ 等于 __doc__,因为它在当前命名空间中定义。因此,为了访问模块对象的属性,不需要获取模块对象本身。 - Oliver Bestwalter

163

7
有没有办法访问调用模块的globals(),而不是定义模块的globals()? - dimo414
10
你可以尝试使用 traceback 模块(http://docs.python.org/library/traceback.html)获取调用者的全局变量,但这会涉及到黑魔法领域。我不知道你想做什么,但如果需要那个,你可能需要重新考虑你的设计。 - Maciej Pasternacki
一个经典的案例是“我需要X(来完成Y)-> 你不需要X,你需要Z”。虽然如此,我还是需要X!请别介意,我只是觉得这很有趣,而且最受欢迎的答案给了我所需的答案 :) - pawamoy
1
需要注意的是,globals() 函数返回的结果可能不正确,因为它取决于调用它的上下文。例如,如果从类函数中调用它,则会返回与类关联的全局上下文,而不是当前模块上下文,这是完全不同的事情。即使从自由函数中调用它,它也可能返回不同的模块全局上下文,这取决于函数的导入方式。 - Andry

6

可能已经有点晚了,但是我自己也没有找到正确的答案。 在Python 3.7.x中,最接近和准确的解决方案(比inspect.stack()更快):

# search for first module in the stack
stack_frame = inspect.currentframe()
while stack_frame:
  print('***', stack_frame.f_code.co_name, stack_frame.f_code.co_filename, stack_frame.f_lineno)
  if stack_frame.f_code.co_name == '<module>':
    if stack_frame.f_code.co_filename != '<stdin>':
      caller_module = inspect.getmodule(stack_frame)
    else:
      # piped or interactive import
      caller_module = sys.modules['__main__']
    if not caller_module is None:
      #... do something here ...
    break
  stack_frame = stack_frame.f_back
优点
  • globals() 方法更精确。
  • 不依赖于栈中间框架,例如通过钩子或第三方工具(如 pytest)添加的框架:
*** foo ... ..
*** boo ... ..
*** runtest c:\python\x86\37\lib\site-packages\xonsh\pytest_plugin.py 58
*** pytest_runtest_call c:\python\x86\37\lib\site-packages\_pytest\runner.py 125
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** <lambda> c:\python\x86\37\lib\site-packages\_pytest\runner.py 201
*** from_call c:\python\x86\37\lib\site-packages\_pytest\runner.py 229
*** call_runtest_hook c:\python\x86\37\lib\site-packages\_pytest\runner.py 201
*** call_and_report c:\python\x86\37\lib\site-packages\_pytest\runner.py 176
*** runtestprotocol c:\python\x86\37\lib\site-packages\_pytest\runner.py 95
*** pytest_runtest_protocol c:\python\x86\37\lib\site-packages\_pytest\runner.py 80
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** pytest_runtestloop c:\python\x86\37\lib\site-packages\_pytest\main.py 258
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** _main c:\python\x86\37\lib\site-packages\_pytest\main.py 237
*** wrap_session c:\python\x86\37\lib\site-packages\_pytest\main.py 193
*** pytest_cmdline_main c:\python\x86\37\lib\site-packages\_pytest\main.py 230
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** main c:\python\x86\37\lib\site-packages\_pytest\config\__init__.py 90
*** <module> c:\Python\x86\37\Scripts\pytest.exe\__main__.py 7
  • 能够处理Python的管道或交互式会话。

缺点:

  • 它非常精确,可以返回在可执行文件中注册的模块,例如pytest.exe,但这可能不是您想要的。
  • inspect.getmodule仍然可能根据挂钩在有效模块上返回None。

我有一个Python扩展: 如何在给定完整路径的情况下导入模块?

该扩展具有用于该情况的包装器函数:

def tkl_get_stack_frame_module_by_offset(skip_stack_frames = 0, use_last_frame_on_out_of_stack = False):
  ...

def tkl_get_stack_frame_module_by_name(name = '<module>'):
  ...

你只需要正确初始化扩展即可:
# portable import to the global space
sys.path.append(<path-to-tacklelib-module-directory>)
import tacklelib as tkl

tkl.tkl_init(tkl, global_config = {'log_import_module':os.environ.get('TACKLELIB_LOG_IMPORT_MODULE')})

# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()

# use `tkl_*` functions directly from here ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接