Python __getattr__ 执行多次

2023-12-01

我一直在努力实施__getattr__函数如下例所示:

PEP 562 -- 模块__getattr__ and __dir__

我不明白为什么这段简单的代码:

# lib.py

def __getattr__(name):
    print(name)

# main.py

from lib import test

outputs:

__path__
test
test

What is __path__?为什么会被发送到__getattr__?为什么是test发送2次?


TL;DR 打印的第一个“测试”是“from import”实现的副作用,即它是在创建期间打印的lib模块。第二个“测试”是直接从模块上的动态属性的后续访问。

知道importlib是用Python代码实现的,修改你的lib.py稍微也转储痕迹:

# lib.py
from traceback import print_stack

def __getattr__(name):
    print_stack()
    print(name)
    print("-" * 80)

这给出了精确定位 importlib 中触发双属性访问的库位置的提示:

$ python3 main.py 
  File "main.py", line 3, in <module>
    from lib import test
  File "<frozen importlib._bootstrap>", line 1019, in _handle_fromlist
  File "/private/tmp/lib.py", line 5, in __getattr__
    print_stack()
__path__
--------------------------------------------------------------------------------
  File "main.py", line 3, in <module>
    from lib import test
  File "<frozen importlib._bootstrap>", line 1032, in _handle_fromlist
  File "/private/tmp/lib.py", line 5, in __getattr__
    print_stack()
test
--------------------------------------------------------------------------------
  File "main.py", line 3, in <module>
    from lib import test
  File "/private/tmp/lib.py", line 5, in __getattr__
    print_stack()
test
--------------------------------------------------------------------------------

现在我们可以通过RTFS轻松找到答案(下面我使用Python v3.7.6,如果版本不同,请将git切换到您使用的确切标签)。在看importlib._bootstrap. _handle_fromlist在指定的行号处。

_handle_fromlist是一个帮助程序,用于加载包子模块from进口。第 1 步是查看模块是否是一个包:

if hasattr(module, '__path__'):

The __path__访问就在那里,在线 1019。因为你__getattr__回报None对于所有输入,hasattr回报True在这里,你的模块看起来像一个包,代码继续。 (如果hasattr已经回来了False, _handle_fromlist此时将中止。)

这里的“fromlist”将包含您请求的名称,["test"],所以我们进入 for 循环x="test"第 1032 行有“额外”调用:

elif not hasattr(module, x):

from lib import test只会尝试加载lib.test子模块如果lib还没有test属性。这个检查是测试该属性是否存在,看看是否_handle_fromlist需要尝试加载子模块。

如果您第一次和第二次调用返回不同的值__getattr__名称为“test”,那么返回的第二个值就是实际收到的值main.py.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python __getattr__ 执行多次 的相关文章

随机推荐