Python __getattr__ executed multiple times

9 浏览
0 Comments

Python __getattr__ executed multiple times

我一直在尝试实现如下示例中的__getattr__函数:\nPEP 562 -- 模块__getattr____dir__\n但是我不明白为什么这段简单的代码会输出:\n

# lib.py
def __getattr__(name):
    print(name)
# main.py
from lib import test

\n输出结果是:\n

__path__
test
test

\n__path__是什么?为什么它会被发送到__getattr__中?为什么test会被发送两次?

0
0 Comments

Python __getattr__ executed multiple times的问题出现的原因是,在进行from import时,导入的模块会被创建,而创建过程中会执行__getattr__方法。解决方法是,在lib.py文件中进行修改,在__getattr__方法中添加一个trace,以便定位触发多次属性访问的库位置。

具体的解决方法如下:

在lib.py文件中添加以下代码:

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

运行main.py文件后,会得到以下输出:

  File "main.py", line 3, in 
    from lib import test
  File "", line 1019, in _handle_fromlist
  File "/private/tmp/lib.py", line 5, in __getattr__
    print_stack()
__path__
--------------------------------------------------------------------------------
  File "main.py", line 3, in 
    from lib import test
  File "", line 1032, in _handle_fromlist
  File "/private/tmp/lib.py", line 5, in __getattr__
    print_stack()
test
--------------------------------------------------------------------------------
  File "main.py", line 3, in 
    from lib import test
  File "/private/tmp/lib.py", line 5, in __getattr__
    print_stack()
test
--------------------------------------------------------------------------------

通过上述输出可以得到线索,可以通过RTFS(Read The Fucking Source)在importlib库中找到问题的答案。在importlib._bootstrap._handle_fromlist的代码中,我们可以看到以下内容:

if hasattr(module, '__path__'):

在这里,我们可以看到对__path__属性的访问,这是问题出现的地方。因为__getattr__方法对所有的输入返回None,所以这里的hasattr返回了True,所以模块看起来像是一个包,代码会继续进行。(如果hasattr返回了False,_handle_fromlist将会在此处中止。)

在这里,"fromlist"将会是你请求的名称,即["test"],所以我们进入了for循环,x="test",在1032行处有一个额外的调用:

elif not hasattr(module, x):

只有当lib没有名为test的属性时,from lib import test才会尝试加载lib.test子模块。这个检查是为了测试属性是否存在,以确定_handle_fromlist是否需要尝试加载子模块。

如果你在name为"test"的第一次和第二次调用__getattr__方法时返回不同的值,那么实际上会接收到第二次返回的值。

通过以上分析,我们可以得出结论,Python中的__getattr__方法在进行from import时会被执行多次的问题是由于importlib库在创建模块时触发了多次属性访问的副作用导致的。要解决这个问题,可以在__getattr__方法中添加适当的判断,以避免重复执行多次。

0