Python中import foo.bar as bar与from foo import bar的区别及懒加载影响
import foo.bar as bar vs from foo import bar:核心区别(含懒导入影响) 嘿,这个问题问到点子上了!很多人觉得这俩导入方式没啥区别,但深究起来,尤其是涉及懒导入的时候,差异还真不小。我给你拆解清楚:
一、常规即时导入(Python默认行为)下的细微区别
首先说Python默认的“导入即加载”情况,这时候两者的最终结果看起来差不多,但底层逻辑有差异:
命名空间与加载逻辑:
import foo.bar as bar:直接按完整模块路径foo.bar查找,先加载父模块foo,再加载子模块foo.bar,最后把foo.bar模块对象绑定到当前命名空间的bar上。注意:此时foo模块已经在内存里了,但当前命名空间不会出现foo这个名字(除非你之前单独导入过)。from foo import bar:先加载父模块foo,然后检查foo模块里有没有bar这个属性。如果没有(而foo是包),Python会自动去foo的子模块里找,找到foo.bar后加载它,把它绑定到foo.bar属性,再导入到当前命名空间的bar上。
简单说,这种情况下两者都会一次性加载
foo和foo.bar,当前命名空间的bar都是foo.bar模块的引用,差异主要在导入时的路径解析顺序,但实际使用起来区别不大。
二、懒导入(延迟加载)下的关键差异
这才是你最关心的部分!懒导入是指模块在第一次被实际使用时才加载,而不是导入语句执行时。这种场景下,两种导入方式的行为天差地别:
import foo.bar as bar的懒加载表现:
执行这条导入语句时,Python只会创建一个“占位”的懒加载模块对象赋值给bar,foo和foo.bar都不会立即加载。只有当你第一次访问bar的属性(比如调用bar.my_function()、访问bar.my_var)时,才会触发连锁加载:先加载父模块foo,再加载子模块foo.bar。
简单说:完全延迟,啥都不加载,等用的时候才全部加载。from foo import bar的懒加载表现:
执行这条语句时,Python首先需要获取foo模块对象才能查找bar,所以父模块foo会立即被加载(如果之前没加载过的话)。而bar(也就是foo.bar子模块)仍然是懒加载状态,只有当你第一次使用bar时才会加载它。
简单说:父模块先加载,子模块延迟加载。
额外补充:特殊情况
如果foo模块的__init__.py里显式定义了一个叫bar的非模块对象(比如变量、函数、类),那from foo import bar会导入这个对象,而import foo.bar as bar还是会导入子模块foo.bar——不过你已经明确foo.bar是模块,这个情况了解就行。
内容的提问来源于stack exchange,提问作者John Oxley

