既然小权权都问了,正好今下午有点时间,那就答一下吧。:-D
先看bluesprint_factory.py的代码做了什么事情:定义了一个 Wrapper 类;
定义了一个 AutoBlueprint 类;
把 bluesprint_factory 模块做了一次封装并替换。
再详细看上述第3步做了什么事:
sys.modules[__name__] = Wrapper(sys.modules[__name__])先从 sys.modules 中得到代表当前py模块被解析后的module对象,先称之为origin_module;
将origin_module对象传递给Wrapper类,得到一个wrapper 对象,称之为wrapped_module,该对象只具有一个属性self.wrapped,其值为origin_module ;
再把wrapped_module对象绑定给 sys.modules[__name__] 这个变量。
总之,经过最后一行代码的执行后:sys.modules[__name__] = wrapped_module
wrapped_module.wrapped = orign_module
到此为止,bluesprint_factory.py 的初始化工作就完成了。
最后,再看从别的模块 from bluesprint_factory import bl会发生什么:当执行 from x import y 的时候,Python 需要先判断 x 是否为package,而判断条件就是 x 是否具有 __path__属性 ,不论__path__的值如何。;
尝试访问 bluesprint_factory 模块对象——wrapped_module ——的__path__属性;
由于wrapped_module被实例化之后,并无__path__ 属性,进入 wrapped_module.__getattr__() ;此处就是你要的答案,为何 __getattr__ 拦截到了"__path__" ,因为 Python 的 import 机制去尝试访问了这个实例化后并不存在的属性;
wrapped_module.__getattr__() 中的 if 判断第一次显然会失败,然后走到 return getattr(self.wrapped, key) ,等价于 return getattr(orign_module, '__path__') ;
明显,上一步 return 之后的控制权限又回到了 Python 的 import 机制中,而且它得到None ,因为 orign_module 并非package,没有 '__path__'属性;
此时,import 机制知道了 from bluesprint_factory中的 bluesprint_factory就是一个正常的module,而非package,然后尝试从 bluesprint_factory 这个module 中获取bl属性;
接着需要拿到 bluesprint_factory的 module 对象,此时拿到的是 wrapped_module,因为你已经把sys.modules里的那个对象给替换了;
然后相当于要取得 wrapped_module.bl,同理,bl在初始化时并不存在,于是进入 wrapped_module.__getattr__()此时的key,已经成了'bl' ;
于是 if key == 'bl' 条件成立,调用AutoBlueprint 类创建其实例。
后续AutoBlueprint 里的操作就不用再解释了吧。
经过上述解释后,应该可以知道,除了from bluesprint_factory import bl 可以触发上述流程来很trick地创建bl对象,import bluesprint_factory; bl = bluesprint_factory.bl 同样可以。
这整个导入流程都是由 Python 的 import 机制控制,相关源代码参考cpython/Python/import.c 、cpython/Lib/importlib 。
-------------------分割线---------------
请问题主我可否将这个问题和我的答案整理后转发到我的微信公众号:jushuoms(驹说码事)?欢迎 Pythonor 关注我的公众号。