导入模块时除了使用模块名进行导入,还可使用目录名进行导入。例如,在sys.path路径下,有一个dir1/dir2/mod.py模块,那么在任意位置处均可以使用下面这种方式导入这个模块。html
import dir1.dir2.mod from dir1.dir2.mod import XXX
一个实际一点的示例,设置PYTHONPATH环境变量为d:\pypath
,而后在此目录下建立以上目录和mod.py文件:python
set PYTHONPATH="D:\pypath" mkdir d:\pypath\dir1\dir2 echo print("mod.py") >d:\pypath\dir1\dir2\mod.py echo x=3 >>d:\pypath\dir1\dir2\mod.py # 进入交互式python >>> import dir1.dir2.mod mod.py >>> dir1.dir2.mod.x 3
注1:在python3.3版本及更高版本是能够导入成功的,可是在python3.3以前的版本将失败,由于缺乏__init__.py
文件,稍后会解释该文件
注2:顶级目录dir1必须位于sys.path列出的路径搜索列表下测试
若是输出dir1和dir2,将会看到它们的是模块对象,且是名称空间。ui
>>> import dir1.dir2.mod mod.py >>> dir1 <module 'dir1' (namespace)> >>> dir1.dir2 <module 'dir1.dir2' (namespace)> >>> dir1.dir2.mod <module 'dir1.dir2.mod' from 'd:\\pypath\\dir1\\dir2\\mod.py'>
这种模块+名称空间
的形式就是包(严格地说是包的一种形式),也就是说dir1是包,dir2也是包,这种方式是包的导入形式。包主要用来组织它里面的模块。spa
从上面的结果也能够看出,包也是模块,因此能使用模块的地方就能使用包。例以下面的代码,能够像导入模块同样直接导入包dir2,包和模块的区别在于它们的组织形式不同,模块可能位于包内,仅此而已。操作系统
import dir1.dir2 from dir1 import dir2
另外,导入dir1.dir2.mod时,它声明的模块变量名为dir1,而不是dir1.dir2.mod,可是导入的对象却包含了3个模块:dir一、dir1.dir2以及dir1.dir2.mod。以下:code
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'dir1'] >>> for key in sys.modules: ... if key.startswith("dir1"): ... print(key,":",sys.modules[key]) ... dir1 : <module 'dir1' (namespace)> dir1.dir2 : <module 'dir1.dir2' (namespace)> dir1.dir2.mod : <module 'dir1.dir2.mod' from 'd:\\pypath\\dir1\\dir2\\mod.py'>
上面的dir1和dir1.dir2目前是空包,或者说是空模块(再一次强调,包就是模块)。但并不意味着它们对应的模块对象是空的,由于模块是对象,只要是对象就会有属性。例如,dir1包有以下属性:htm
>>> dir(dir1) ['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'dir2']
之因此称为空包,是由于它们如今仅提供了包的组织功能,并且它们是目录,而不像py文件同样,是实实在在的能够编写模块代码的地方。换句话说,包如今是目录文件,而不是真正的模块文件。对象
为了让包"真正的"成为模块,须要在每一个包所表明的目录下加入一个__init__.py
文件,它表示让这个目录格式的模块(也就是包)像py文件同样能够写模块代码,只不过这些模块代码是写入__init__.py
中的。固然,模块文件中容许没有任何内容,因此__init__.py
文件也能够是空文件,它仅表示让包成为真正的模块文件。blog
每次导入包的时候,若是有__init__.py
文件,将会自动执行这个文件中的代码,就像模块文件同样,事实上它就是让目录表明的包变成模块的,甚至能够说它就是包所对应的模块文件(见下面示例),因此也能够认为__init__.py
是包的初始化文件。在python3.3以前,这个文件必须存在,不然就会报错,由于它不认为目录是有效的模块。
如今,在dir1和dir2下分别建立空文件__init__.py
:
type nul>d:\pypath\dir1\__init__.py type nul>d:\pypath\dir1\dir2\__init__.py
如今目录的层次格式以下:
λ tree /f d:\pypath D:\PYPATH └─dir1 │ __init__.py └─dir2 mod.py __init__.py
再去执行导入操做,并输出包dir1和dir2。
>>> import dir1.dir2.mod mod.py >>> dir1 <module 'dir1' from 'd:\\pypath\\dir1\\__init__.py'> >>> dir1.dir2 <module 'dir1.dir2' from 'd:\\pypath\\dir1\\dir2\\__init__.py'> >>> dir1.dir2.mod <module 'dir1.dir2.mod' from 'd:\\pypath\\dir1\\dir2\\mod.py'>
从输出结果中不难看出,包dir1和dir1.dir2是模块,且它们的模块文件是各自目录下的__init__.py
。
实际上,包分为两种:名称空间模块、普通模块。名称空间包是没有__init__.py
文件的,普通包是有__init__.py
文件的。不管是哪一种,它都是模块。
既然包是模块,而__init__.py
文件是包的模块文件,这个文件中应该写入什么代码?答案是能够写入任何代码,咱们只需把它看成一个模块对待就能够。不过,包既然是用来组织模块的,真正的功能性属性应该尽可能写入到它所组织的模块文件中(也就是示例中的mod.py)。
但有一项__all__
是应该在__init__.py
文件中定义的,它是一个列表,用来控制from package import *
使用*
导入哪些模块文件。这里的*
并不是像想象中那样会导入包中的全部模块文件,而是只导出__all__
列表中指定的模块文件。
例如,在dir1.dir2包下有mod1.py、mod2.py、mod3.py和mod4.py,若是在dir2/__init__.py
文件中写入:
__all__ = ["mod1", "mod2", "mod3"]
则执行:
from dir1.dir2 import *
不会导入mod4,而是只导入mod1-mod3。
若是不设置__all__
,则from dir1.dir2 import *
不会导入该包下的任何模块,但会导入dir1和dir1.dir2。
严格地说,只有当某个模块设置了__path__
属性时,才算是包,不然只算是模块。这是包的绝对严格定义。
__path__
属性是一个路径列表(可迭代对象便可,但一般用列表),和sys.path
相似,该列表中定义了该包的初始化模块文件__init__.py
的路径。
只要导入的是一个包(不管是名称空间包仍是普通包),首先就会设置该属性,默认导入目录时该属性会初始化当前目录,而后去该属性列出的路径下搜索__init__.py
文件对包进行初始化。默认状况下因为__init__.py
文件后执行,在此文件中能够继续定义或修改__path__
属性,使得python会去找其它路径下的__init__.py
对模块进行初始化。
如下是默认初始化后的__path__
值:
>>> import dir1.dir2 >>> dir1.dir2.__path__ ['d:\\pypath\\dir1\\dir2'] >>> import dir1.dir3 >>> dir1.dir3 <module 'dir1.dir3' (namespace)> >>> dir1.dir3.__path__ _NamespacePath(['d:\\pypath\\dir1\\dir3'])
通常来讲,几乎不会设置__path__
属性。
import和from导入时有多种语法可用,这两个语句的导入方式和导入普通模块的方式是同样的:import导入时须要使用前缀名称去引用,from导入时是赋值到当前程序的同名全局变量中。若是不了解,请看前一篇文章:python模块导入细节。
假设如今有以下目录结构,且d:\pypath位于sys.path列表中:
$ tree -f d:\pypath d:\pypath └── dir1 ├── __init__.py └── dir2 ├── __init__.py └── mod.py
只导入包:
import dir1 # 导入包dir1 import dir1.dir2 # 导入包dir1.dir2 from dir1 import dir2 # 导入包dir1.dir2
导入某个模块:
import dir1.dir2.mod from dir1.dir2 import mod
若是dir2/__init__.py
中设置了__all__
,则下面的导入语句会导入已设置的模块:
from dir1.dir2 import *
注意,只支持上面这种from...import *
语法,不支持import *
。
导入模块中的属性,好比变量x:
from dir1.dir2.mod import x
注:若是容许,不要使用相对路径导入,很容易出错,特别是对新手而言。使用绝对路径导入,并将包放在sys.path的某个路径下就能够。
假设如今有以下目录结构:
$ tree -f d:\pypath d:\pypath └── dir1 ├── __init__.py ├── dir4 │ ├── __init__.py │ ├── c2.py │ └── c1.py ├── dir3 │ ├── __init__.py │ ├── b3.py │ ├── b2.py │ └── b1.py └── dir2 ├── __init__.py ├── a4.py ├── a3.py ├── a2.py └── a1.py
在dir1.dir2.a1模块文件中想要导入dir1.dir3.b2模块,能够在a1.py中使用下面两种方式导入:
import dir1.dir3.b2 from dir1.dir2. import b2
上面的导入方式是使用绝对路径进行导入的,只要使用绝对路径,都是从sys.path开始搜索的。例如,上面是从sys.path下搜索dir1,再依次搜索dir1.dir3.b2。
python还支持包的相对路径的导入,只要使用.
或..
便可,就像操做系统上的相对路径同样。使用相对路径导入时不会搜索sys.path。
相对路径导入方式只有from...import
支持,import
语句不支持,且只有使用.
或..
的才算是相对路径,不然就是绝对路径,就会从sys.path下搜索。
例如,在a1.py中导入dir1.dir3.b2:
from ..dir3 import b2
注意,必须不能直接python a1.py
执行这个文件,这样会报错:
from ..dir3 import b2 ValueError: attempted relative import beyond top-level package
报错缘由稍后解释。如今在交互式模式下导入,或者使用python -m dir1.dir2.a1
的方式执行。
>>> import dir1.dir2.a1
如下几个示例都如此测试。
在a1.py中导入包dir3:
from .. import dir3
在a1.py中导入dir1.dir2.a2,也就是同目录下的a2.py:
from . import a2
导入模块的属性,如变量x:
from ..dir3.b2 import x from .a2 import x
前面说过一个相对路径导入时的错误:
from ..dir3 import b2 ValueError: attempted relative import beyond top-level package
dir3明明在dir1下,在路径相对上,dir3确实是a1.py的../dir3
,但执行python a1.py
为何会报错?
from ..dir3 import b2
这是由于文件系统路径并不真的表明包的相对路径,当在dir1/a1.py中使用..dir3
,python并不知道包dir1的存在,由于没有将它导入,没有声明为模块变量,一样,也不知道dir2的存在,仅仅只是根据语句知道了dir3的存在。但由于使用了相对路径,不会搜索sys.path,因此它的相对路径边界只在本文件。因此,下面的导入也是错误的:
from . import a2
实际上,更标准的解释是,当py文件做为可执行程序文件执行时,它所在的模块名为__main__
,即__name__
为__main__
,但它并不是一个包,而是一个模块文件,对它来讲没有任何相对路径可言。
解决方法是显式导入它们的父包,让python记录它的存在,只有这样才能使用..
:
python -m dir1.dir2.a2
还有几个常见的相对路径导入错误:
from .a3 import x
错误:
ModuleNotFoundError: No module named '__main__.a3'; '__main__' is not a package
缘由是同样的,py文件做为可执行程序文件执行时,它所在的模块名为__main__
,它并不是一个包。
最后,建议在条件容许的状况下,使用绝对路径导入,而不是相对路径。
经过包的导入方式也支持别名。例如:
from dir1.dir2.a2 import x as xx print(xx) import dir1.dir2.a2 as a2 print(a2.x) from dir1.dir2 import a2 as a22 print(a22.x)