Python __getattr__ 执行多次

标签 python getattr

我一直在尝试实现 __getattr__ 函数,如下例所示:

PEP 562 -- Module __getattr__ and __dir__

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

# lib.py

def __getattr__(name):
    print(name)

# main.py

from lib import test

输出:

__path__
test
test

什么是 __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__'):

__path__ 访问出现在第 1019 行。因为您的 __getattr__ 为所有输入返回 Nonehasattr 在这里返回 True,所以你的模块看起来像一个包,代码继续。 (如果 hasattr 返回了 False_handle_fromlist 将在此时中止。)

此处的“fromlist”将包含您请求的名称,["test"],因此我们使用 x="test" 进入 for 循环并在第 1032 行有一个“额外的”调用:

elif not hasattr(module, x):

from lib import test 只会在 lib 还没有 test< 时尝试加载 lib.test 子模块 属性。此检查正在测试该属性是否存在,以查看 _handle_fromlist 是否需要尝试加载子模块。

如果第一次和第二次调用名称为“test”的__getattr__ 返回不同的值,那么返回的第二个值将实际在main.py< 中接收.

关于Python __getattr__ 执行多次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60774515/

相关文章:

python - 使用 __getattr__ 和 getattr 的无限循环

python - 如何让 PyC​​harm 在方法中自动完成代码?

python - 分组依据 分组依据 和 平均值

python - scipy中的偏斜正态分布

python - pandas 根据条件执行操作 - 不同的方式和最佳实践?

python - getattr() 与 __dict__ 查找,哪个更快?

python - 如何在Python中从shell中获取变量?

python - 方法 __getattr__ 不是从父类继承的

python - 如何使用 getattr 从模型中获取外键值

python - 覆盖 getattr 和 setattr 的方式的根本区别