python的相对引入和绝对引入

来源:互联网 发布:linux shell expect 编辑:程序博客网 时间:2024/06/05 13:30

python的模块导入方式

import moudle:隐式相对导入,implicit relative import,从本package中寻找同名moudle。

form . import moudle:显式相对导入,explicit relative import,从指定的package中寻找同名moudle。

from package import moudle:绝对导入,absolute import,从sys.path中寻找同名moudle。

from __feature__ import absolute_import:py3之前版本声明绝对导入,实质是禁用隐式相对导入(隐式相对导入看作是绝对导入)。


文件目录结构

TestPackage1/

|----------------run.py

|----------------Package1/

|----------------------------__init__.py

|----------------------------Moudle1P1.py
|----------------------------Moudle2P1.py

|----------------------------run_P1.py

|----------------------------Package2/

|----------------------------------------__init__.py

|----------------------------------------Moudle1P2.py
|----------------------------------------Moudle2P2.py
|----------------------------------------Moudle3P2.py

|----------------------------Package3/

|----------------------------------------__init__.py
|----------------------------------------Moudle1P3.py
|----------------------------------------Moudle2P3.py
|----------------------------------------test.py

通过上面的某结构可以看到,Package1/Package2/Package3是三个包,TestPackage不是包。

各个模块的代码

test.py
  1 #coding=utf-8  2 #from __future__ import absolute_import  3 print ("__file__:  %s" %__file__)  4 print ("__package__:  %s" %__package__)  5 print ("__name__:  %s" %__name__)  6 import sys  7 #del sys.path[0]  8 #sys.path.insert(0,'/mnt/disk2/zhaobin/TestPackage/Package1/Package3')  9 #sys.path.append('/mnt/disk2/zhaobin/TestPackage/Package1/Package3') 10 print ("sys.path:  %s" %sys.path) 11 #from .. import Package2 12 #from . import Moudle2 13 #from ..Package2 import Moudle2P2 14 import Moudle2P3 15 #from Package3 import Moudle2P3 16 #print (Moudle2.__file__) 17 #from ..Package2 import test 18 def main(): 19     pass

runP1.py

  1 #import Package3.test  2 #import Package3.test  3 from Package3 import test  4   5 if __name__=="__main__":  6     test.main()
MoudlexPy

 1 print ("i am Packagey moudlex") 


关于相对引入和绝对引入的几组实验

实验中python版本为python27;python3版本为python33。

执行python test.py的输出为:

__file__:  test.py__package__:  None__name__:  __main__sys.path:  ['/mnt/disk2/zhaobin/TestPackage/Package1/Package3', '/mnt/disk2/anaconda/lib/python27.zip', '/mnt/disk2/anaconda/lib/python2.7', '/mnt/disk2/anaconda/lib/python2.7/plat-linux2', '/mnt/disk2/anaconda/lib/python2.7/lib-tk', '/mnt/disk2/anaconda/lib/python2.7/lib-old', '/mnt/disk2/anaconda/lib/python2.7/lib-dynload', '/mnt/disk2/anaconda/lib/python2.7/site-packages', '/mnt/disk2/anaconda/lib/python2.7/site-packages/PIL', '/mnt/disk2/anaconda/lib/python2.7/site-packages/Sphinx-1.2.3-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/runipy-0.1.1-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/setuptools-5.8-py2.7.egg']i am package3 moudle2

关注输出中的以下几个方面:

__name__变量为__main__,sys.path的第一个值为脚本被执行的绝对路径,输出的最后一行为moudle2P3所在的位置。此时我们有个疑问,被引入的这个moudle2P3是绝对引入还是相对引入?只需要将sys.path列表中的第一个值去掉(也就是把test.py中的第7行注释去掉)后再运行python test.py就可以验证,如果输出的最后一行依旧是"i am package3 moudle2",则说明是相对路径引入模块,实际输出为:

__file__:  test.py__package__:  None__name__:  __main__sys.path:  ['/mnt/disk2/anaconda/lib/python27.zip', '/mnt/disk2/anaconda/lib/python2.7', '/mnt/disk2/anaconda/lib/python2.7/plat-linux2', '/mnt/disk2/anaconda/lib/python2.7/lib-tk', '/mnt/disk2/anaconda/lib/python2.7/lib-old', '/mnt/disk2/anaconda/lib/python2.7/lib-dynload', '/mnt/disk2/anaconda/lib/python2.7/site-packages', '/mnt/disk2/anaconda/lib/python2.7/site-packages/PIL', '/mnt/disk2/anaconda/lib/python2.7/site-packages/Sphinx-1.2.3-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/runipy-0.1.1-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/setuptools-5.8-py2.7.egg']i am anaconda module2
可见最后一行为"i am anaconda moudle2",这是因为在目录'/mnt/disk2/anaconda/lib/python2.7'有一个Moudle2P3.py。如果把这个Moudle2P3.py删除,运行时会报错:找不到模块moudle2P3。

保持sys.path列表中第一项去掉,然后执行python runP1.py,输出为:

__file__:  /mnt/disk2/zhaobin/TestPackage/Package1/Package3/test.pyc__package__:  None__name__:  Package3.testsys.path:  ['/mnt/disk2/anaconda/lib/python27.zip', '/mnt/disk2/anaconda/lib/python2.7', '/mnt/disk2/anaconda/lib/python2.7/plat-linux2', '/mnt/disk2/anaconda/lib/python2.7/lib-tk', '/mnt/disk2/anaconda/lib/python2.7/lib-old', '/mnt/disk2/anaconda/lib/python2.7/lib-dynload', '/mnt/disk2/anaconda/lib/python2.7/site-packages', '/mnt/disk2/anaconda/lib/python2.7/site-packages/PIL', '/mnt/disk2/anaconda/lib/python2.7/site-packages/Sphinx-1.2.3-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/runipy-0.1.1-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/setuptools-5.8-py2.7.egg']i am package3 moudle2
从输出中可以看到__name__的值为Package3.test,最后一行表示moudle2P3.py的位置是在Package3下面,而不是在anaconda中。为什么运行python test.py和运行python runP1.py所引用的moudle2P3不同?这是问题一。接下来看运行python3 runP1.py的输出值:

__file__:  /mnt/disk2/zhaobin/TestPackage/Package1/Package3/test.py__package__:  Package3__name__:  Package3.testsys.path:  ['/home/ndir/python3/lib/python33.zip', '/home/ndir/python3/lib/python3.3', '/home/ndir/python3/lib/python3.3/plat-linux', '/home/ndir/python3/lib/python3.3/lib-dynload', '/home/ndir/python3/lib/python3.3/site-packages']i am python3.3 moudle2
可以看到,__name__值与上面的输出是相同的,而moudle2P3的位置是在~/python3/lib/python3.3中。为什么运行python runP1.py和运行python3 runP1.py所引用的moudle2P3不同?这是问题二。

下面将阐述原因所在:

先看一段话:Note that both explicit and implicit relative imports are based on the name of the current module. Since the name of the main module is always"__main__",modules intended for use as the main module of a Python application should always use absolute imports.

所谓的相对路径(显式/隐式)是相当于当前模块的路径,但是直接执行脚本会使脚本中的__name__值变成__main__而不是模块的名字,这样的话就没有层次结构,也就无从谈起相对路径了。相对引用和 if __name__ == "__main__"是不能共存的,这就说明问题一的原因。python3之前的版本虽然默认先执行相对引入,但是直接执行脚本导致相对引入无效,只能进行绝对引入,也就是从sys.path中的路径去寻找moudle(正如最开始的两个输出,moudle的位置是其在绝对路径中的位置)。如果我们运行python runP1.py时,test.py作为模块被导入到runP1.py主模块中,test.py的__name__为Package3.test。这个时候test.py中的import moudle2P3是默认先相对引入的(py3版本之前的特性),于是输出的最后一行为:i am package3 moudle2。而运行python3 runP1.py时,import moudle默认是绝对引入(py3版本的特性),于是输出的最后一行为:i am python3.3 moudle2。问题一二就都解决了。

运行代码报错

ValueError: Attempted relative import in non-package

这个错误出现的还原,让test.py中的第7/14行被注释,第12行解注释,运行python test.py时就是报这个错。报错的原因是引入模块时采用的是显式相对引入,而pyton中是通过__name__来决定它在包中的位置,此时__name__为__main__,是无法进行相对引入的,而此时采用显式相对引入所以会报出这个错误。而我们运行python runP1.py的时候这个错误就消失了,这时test.py被当做模块被导入,此时__name__的值是模块相对于被执行脚本的路径,在此实例中为Package3.test(脚本的__file__在目录Package1下面)。于是,执行相对引入时是可以找到相对路径的。

ValueError: Attempted relative import beyond toplevel package

这个错误出现的还原,让test.py中的第7/14行被注释,第13行解注释,运行python runP1.py就会报这个错。报错的原因可以从下面两个角度来解释:角度一,Package1为一个包,当直接执行python runP1.py时就没有把Package1当做包来处理,于是下层包就找不到上层的包了。角度二,通过运行结果输出,我们可以看到test.py中的__name__值为Package3.test,也就是说从test.py出发,只能定位到Package3这一层,所以from . import moudle2P3是可以的,而from .. 是要定位到Package3的上一层,是无法找到的。我更加青睐于第二种理解。解决办法是执行TestPackage中的run.py,其代码为
#import Package1.Package3.testfrom Package1.Package3 import testif __name__=="__main__":    test.main()
执行python run.py时,这个错误就会消失,通过输出可以看到test.py的__name__为Package1.Package3.test,通过角度二来理解就是from .. 是可以定位到Package1的。发散一下,如果test.py中改成from ... 又会报错吧。输出如下:
__file__:  /mnt/disk2/zhaobin/TestPackage/Package1/Package3/test.py__package__:  None__name__:  Package1.Package3.testsys.path:  ['/mnt/disk2/anaconda/lib/python27.zip', '/mnt/disk2/anaconda/lib/python2.7', '/mnt/disk2/anaconda/lib/python2.7/plat-linux2', '/mnt/disk2/anaconda/lib/python2.7/lib-tk', '/mnt/disk2/anaconda/lib/python2.7/lib-old', '/mnt/disk2/anaconda/lib/python2.7/lib-dynload', '/mnt/disk2/anaconda/lib/python2.7/site-packages', '/mnt/disk2/anaconda/lib/python2.7/site-packages/PIL', '/mnt/disk2/anaconda/lib/python2.7/site-packages/Sphinx-1.2.3-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/runipy-0.1.1-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/setuptools-5.8-py2.7.egg']Traceback (most recent call last):  File "run.py", line 2, in <module>    from Package1.Package3 import test  File "/mnt/disk2/zhaobin/TestPackage/Package1/Package3/test.py", line 13, in <module>    from ...Package1.Package2 import Moudle2P2ValueError: Attempted relative import beyond toplevel package

通过对以上两个常见错误的分析,讲一下__name__的问题,它是python中有一个内置变量,通过判断__name__的具体值来判断模块是否被执行。包含显式相对路径(from package import moudle)的模块是不能直接被执行的,只能作为模块被引用。如果包内有from . import moudle,这个模块必须在其上一层被引用;如果包内有from .. import moudle,这个模块必须在其上两层被引用;如果同层引用时,test.py中的__name__为本模块的名字test,是无法进行显示相对引入。实验输出如下:
__file__:  /mnt/disk2/zhaobin/TestPackage/Package1/Package3/test.py__package__:  None__name__:  testsys.path:  ['/mnt/disk2/anaconda/lib/python27.zip', '/mnt/disk2/anaconda/lib/python2.7', '/mnt/disk2/anaconda/lib/python2.7/plat-linux2', '/mnt/disk2/anaconda/lib/python2.7/lib-tk', '/mnt/disk2/anaconda/lib/python2.7/lib-old', '/mnt/disk2/anaconda/lib/python2.7/lib-dynload', '/mnt/disk2/anaconda/lib/python2.7/site-packages', '/mnt/disk2/anaconda/lib/python2.7/site-packages/PIL', '/mnt/disk2/anaconda/lib/python2.7/site-packages/Sphinx-1.2.3-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/runipy-0.1.1-py2.7.egg', '/mnt/disk2/anaconda/lib/python2.7/site-packages/setuptools-5.8-py2.7.egg']Traceback (most recent call last):  File "run.py", line 3, in <module>    import test  File "/mnt/disk2/zhaobin/TestPackage/Package1/Package3/test.py", line 12, in <module>    from . import Moudle2P3ValueError: Attempted relative import in non-package



未完待续!






























0 0
原创粉丝点击