深入解析Python的import机制:从sys.modules到Local名字空间的全面指南
本文详细介绍了Python的进口机制,这对于理解Python的运行机制非常有帮助!

1。标准导入:
将python中的所有模块都放在sys.modules中。导入模块时,首先在此列表中检查是否已加载模块。如果加载,它只会将模块的名称添加到调用导入的模块的本地名称空间中。如果未加载,请根据SYS.Path目录中的模块名称查找模块文件。该模块可以是PY,PYC,PYD。找到它后,将模块加载到内存中,将其添加到SYS.MODULES,然后将名称导入当前本地名称空间。
模块不会重复加载。多个不同的模块可以使用导入将同一模块导入其本地名称空间。实际上,其背后只有一个pymoduleObject对象。这是一个容易忽略的问题:导入只能导入模块,但不能在模块中导入对象(类,功能,变量等)。例如:模块A(a.py)中有一个函数getName,另一个模块无法通过import a.getName将getName入用到此模块中,因此您只能从import getName中使用。
2.持续的导入:
1)顺序嵌套:此模块导入模块A(导入A),在A中,它导入B,并且模块B可以导入其他模块...
筑巢更容易理解。要注意的一件事是,每个模块的本地名称空间是独立的。对于上述示例,导入A后,此模块只能访问模块A,但无法访问模块B和其他模块。尽管已将模块B加载到内存中,如果访问,则必须在此模块中明确导入B。
2)例如筑巢:
文件[a.py]
从B进口D
C级:通过
文件[B.Py]
从进口c
D级:通过
为什么执行A时不能加载D?
如果将A.Py更改为:导入B,那很好。
这是怎么回事?
罗伯钦(Robertchen):这与Python的进口机理有关。具体而言,从b进口D中,Python将分为几个步骤:
(1)在sys.modules中找到符号“ b”
(2)如果存在符号B,则获得与符号B相对应的模块对象。
获取与__ -dict__符号“ D”相对应的对象,如果不存在“ D”,则会引发异常。
(3)如果符号B不存在,则会创建一个新的模块对象。请注意,此时,模块对象的__ -dict__是空的。
在b.py中执行表达式,填写___dict__。
从“ __ -dict__”的“ D”中获取对应的对象,如果不存在“ D”,则会引发异常。
因此,此示例的执行顺序如下:
1。从a.py中的B导入d执行。由于它被执行python a.py,因此在sys.modules中不存在。首先,为B.Py创建一个模块对象()。请注意,此时创建的模块对象是空的,内部没有任何东西。在Python中创建此模块对象后,将对B.Py进行解析和执行,目的是填充此__Dict__。
2。从b.py的导入c执行执行B.Py时,您会遇到此句子。首先检查sys.modules的模块缓存。由于此时尚未缓存缓存,因此Python为A.Py创建一个模块对象(),然后同样,在A.Py中执行语句
3。再次在A.Py中从B导入d执行。目前,由于创建的对象已在步骤1中被缓存在Sys.Modules中,因此我直接得到了它。但是,请注意,从整个过程中,我都可以看一下。 ,我们知道目前它仍然是一个没有内部的空对象,因此从该模块中获取符号“ D”的操作将引发例外。如果这只是导入b,则由于符号“ b”已经存在于sys.modules中,因此不会抛出任何例外。
ZQ:插图如下:
3。只要在软件包导入的文件夹下有一个__init__.py文件,就可以将此文件夹视为包装。导入软件包的过程基本上与模块的过程相同。但是,在导入软件包时,将执行软件包目录中的__init__.py,而不是模块中的语句。此外,如果您只是简单地导入软件包,并且在软件包__init__.py中没有其他明确的初始化操作,则该软件包下面的模块将不会自动导入。
例如:
有以下软件包结构:
PA
| ----- __init__.py
| ----波
| ----- PB1
| ----- __init__.py
| ------ PB1_M.PY
| ----- PB2
| ----- __init__.py
| ------ PB2_M.Py
有以下程序:
导入系统
导入pa。波#1
导入PA.PB1#2
导入pa.pb1.pb1_m作为m1#3
导入pa.pb2.pb2_m#4
pa.wave.getName()#5
m1.getName()#6
pa.pb.pb2_m.getName()#7
1)执行#1后,SYS.Modules将具有PA和PA.Wave模块。目前,可以调用PA.波的任何类或功能。但是,在PA.pb1(2)下没有模块。目前,本地有一个PA名。
2)执行#2后,只需将pa.pb1加载到内存中即可。 Sys.Modules,Pa.Wave和PA.PB1中将有三个模块,但是在PA.PB1下没有模块自动加载到存储器中。 ,如果直接执行pa.pb1.pb1_m.getName(),则会发生错误,因为pa.pb1中没有pb1_m。当前,本地只有PA名,并且没有PA.PB1名称。
3)执行#3后,将PA.PB1下的PB1_M加载到内存中。 Sys.Modules中将有四个模块:PA,PA.Wave,Pa.pb1和Pa.pb1.pb1_m。 PA目前可以执行。 .pb1.pb1_m.getName()在这里。由于所使用的是,除了PA名之外,还将M1作为PA.pb1.pb1.m在当前本地中的别名。
4)执行#4之后,PA.PB2,PA.PB2.PB2_M将加载到内存中。将有PA,PA.Wave,PA.PB1,PA.PB1.PB1_M,PA.PB2和SYS.Modules中的PA。 .pb2.pb2_m六个模块。目前,本地只有PA和M1。
下面的#5,#6和#7都可以正确运行。
请注意,如果pa.pb2.pb2_m想要导入pa.pb1.pb1_m和pa.wave,它可以直接成功。最好使用清晰的导入路径,对于./的相对导入路径不建议使用。