相对导入和绝对导入是针对包内导入而言的,也就是包内部模块之间的导入。
假设有这么个包结构:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
绝对导入,就是带绝对路径(包内逻辑层次)的导入,例如:
import moduleA.functionA
或者
from moduleX import functionX
相对导入,就是相对于当前文件位置的导入( .代表当前模块层次,…代表上层,…代表上上层(当然,极其不推荐使用…)),例如,在moduleY.py中导入moduleX.py中的functionX:
from .moduleX import functionX
在moduleY.py中导入moduleA
import ..moduleA
好,如何理解相对导入和绝对导入,又有什么问题需要记住理解呢?
首先,记住或理解下面四点:
1、Python导入的搜索路径:
1)在当前目录下搜索
2)在环境变量PYTHONPATH中指定的路径列表中依次搜索
3)在Python安装路径中的lib库中搜索
2、Python完成import的步骤,Python所有加载的模块信息都放在sys.modules结构中,当加载一个模块时:
1)import A,先在sys.modules中搜索是否存在A,存在则不加载不存在则先喂A创建module,并加载。
2)from A import B,先为A创建modules对象,再解析A,从中寻找B,并填充到A的__dict__中。
3、有且只能有一个top_level script,这个top_level script就是最开始运行的那个脚本(python somefile.py)
4、脚本文件在实际文件系统目录结构中的位置,和python解释器认为它所在的位置不是一回事儿!Python解释器利用脚本文件(或者称为模块)的__name__属性来判定它所归属的包。
在记住上面四点后,下面详细说说Python解释器进行相对或绝对调用过程。(原文Relative imports for the billionth time
有两种调用Python脚本文件的方式。
一是直接运行(top_level脚本):
python myfile.py
二是作为模块运行:
python -m myfile.py
#或者
#从其它脚本文件中
import myfile.py
以上两种方式,无论哪一种,在文件被调用时,Python解释器都会给它起个名字并存储在文件的__name__属性中,top_level文件被命名为__main__,模块方式运行的文件被命名为“以所在包/子包作为前缀,并以.作为分隔符,再加上文件名自身”,例如,
from package.subpackage1.moduelX import functionX
此时,moduleX就被命名为“package.subackage.moduleX”,并存储在__name__属性中,根据这个名字属性,Python解释器就会理解moduleX这个模块属于package包中的subpackage子包。
还有一个窍门,模块的名字取决于他是直接从目录中import的还是以包的形式import的,例如,在package/subpackage1这个目录中运行Python解释器,然后输入 import moduleX :
$ cd package/
package $ cd subpackage1
subpackage1 $ python
Python 2.7.10 (default, Feb 7 2017, 00:08:15)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import moduleX
>>> moduleX.__name__
'moduleX'
此时,moduleX的名字就是moduleX而不是package.subpackage1.moduleX,这是因为在启动Python解释器时,Python解释器将当前目录加入到搜索目录中,如果在当前目录下找到了目标文件,解释器不会再做任何上层搜索,也就不会知道当前目录(package1)是其它目录(package)的子目录,也就是说,不知道当前目录是某个包(package)的一部分,因此,包信息就不会被加入到模块的名称属性中。
那么,关键点来了!如果模块名字属性中没有任何的“.”,Python解释器就认为它不属于任何包!
相对引用利用模块名字属性判定它属于哪个包,当使用类似于“from … import foo”语句时,语句中的“.”定义了对应的包层次结构。例如,如果当前模块名称属性是“package.subpackage1.moduleX”,那么“…moduleA”代表的包层次结构就是“package.moduleA”。也就是说,“from … import foo”这语句能正常运行的条件是:包含该语句的模块名称属性必须有“至少和from语句中一样多的 . ”。(有些绕嘴,可以实际写代码试试)
如果必须直接运行moduleX,而且还必须包含包层次信息,这么做:
#首先切换到package上一层目录,然后
$ python -m package.subpackage1.moduleX
如果不需要直接运行moduleX,而是运行其它脚本文件(例如main.py),并在该文件(main.py)中运行moduleX中的函数,解决办法就是:将main.py放在package目录之外,然后运行main.py。
从Python 2.6开始, 不仅是模块的 name 属性包含package信息,同时 package 属性包含package信息
当__package__属性不是None时,模块的 “name” 准确的说是: package + ‘.’ + name,当__package__ 是None时,模块的"name"是__name__。
下面以例子复习上面的内容:
首先是文件系统目录结构
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
main.py
然后是 moduleX.py、moduleA.py和main.py代码
# moduleX.py
print "moduleX's property __name__ is : %s " %__name__
print "moduleX's property __package__ is : %s " %__package__
from .. import moduleA
def functionX():
moduleA.foo()
#moduleA.py
def foo():
print "This is from ModuleA.py, function foo"
print "ModuleA's property __name__ is: %s " %__name__
print "moduleA's property __package__ is : %s " %__package__
#main.py
from package.subpackage1.moduleX import functionX
print "main's property __name__ is: %s" %__name__
print "main's property __package__ is : %s " %__package__
functionX()
开始测试,首先是直接运行main.py
$ python main.py
moduleX's property __name__ is : package.subpackage1.moduleX
moduleX's property __package__ is : None
ModuleA's property __name__ is: package.moduleA
moduleA's property __package__ is : None
main's property __name__ is: __main__
main's property __package__ is : None
This is from ModuleA.py, function foo
$ python -m main
moduleX's property __name__ is : package.subpackage1.moduleX
moduleX's property __package__ is : None
ModuleA's property __name__ is: package.moduleA
moduleA's property __package__ is : None
main's property __name__ is: __main__
main's property __package__ is :
This is from ModuleA.py, function foo
下面以模块方式运行moduleX
$ python -m package.subpackage1.moduleX
moduleX's property __name__ is : __main__
moduleX's property __package__ is : package.subpackage1
ModuleA's property __name__ is: package.moduleA
moduleA's property __package__ is : None
$ python package/subpackage1/moduleX.py
moduleX's property __name__ is : __main__
moduleX's property __package__ is : None
Traceback (most recent call last):
File "package/subpackage1/moduleX.py", line 4, in <module>
from .. import moduleA
ValueError: Attempted relative import in non-package