Python的相对导入和绝对导入

  相对导入和绝对导入是针对包内导入而言的,也就是包内部模块之间的导入。
  假设有这么个包结构:

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

然后是以模块方式运行main.py

$ 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 

再来直接运行moduleX.py

$ 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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值