Python 中包/模块的 `import` 操作代码
用实例来说明import的作用吧。
创建以下包结构。一个文件夹cookFish/,下面包含两个文件,__init__.py和cookBook.py。
为什么取这几个名字呢?假设我想用Python去做和鱼相关的菜,这件事情很复杂,所以我给它创建了一个包,名叫cookFish,既然是包,在它下面必须得创建一个文件__init__.py。烧鱼必备条件之一就是菜谱,所以接着创建了cookBook.py。这几个文件对我们这次来说就足够了,所以就没有再创建其他文件了。
cookFish/ __init__.py cookBook.py
在cookFish/__init__.py中输入如下代码:
__version__='0.1' __author__='XIEByron' defcookFish_hello(): print("cookFish_Hello()fromcookFish/__init__.py")
在cookFish/cookBook.py中输入如下代码:
defcookBook_hello(): print("cookBook_hello()fromcookBook.py")
提示:下面的实例都是在Python自带的命令行解释器(windows+python3.7)中运行的结果。如果你在其他环境下运行,比如jupyternotebook,输出会有差异。
"importpackage-name"都做了什么?
导入包cookFish。
>>>importcookFish
提示:
如果import时出现错误ModuleNotFoundError,如下:
>>>importcookFish Traceback(mostrecentcalllast): File"",line1,in ModuleNotFoundError:Nomodulenamed'cookFish'
建议先将Python的当前工作目录设置为cookFish的父文件夹(就是包含cookFish文件夹的文件夹)。命令如下:
>>>importos >>>os.chdir(r'path\to\parent\folder\of\cookFish')
用dir操作查看当前命名空间和cookFish命名空间下都有哪些内容。
>>>dir()#查看当前命名空间下的对象。注意:cookFish在当前命名空间下。
['__annotations__','__builtins__','__doc__','__loader__','__name__','__package__','__spec__','cookFish','os']
>>>dir(cookFish)#查看cookFish命名空间下的对象。
['__author__','__builtins__','__cached__','__doc__','__file__','__loader__','__name__','__package__','__path__','__spec__','__version__','cookFish_hello']
其中的的__author__,__version__,cookFish_hello是我们定义的,都导入到了cookFish的命名空间下。但是cookFish下的模块cookBook.py没有被导入。这是因为直接importcookFish只运行cookFish文件夹下的__init__.py文件,不会运行其他模块,所以cookBook没有被导入。
提示:Python中的模块指后缀.py的文件,也叫脚本。包指包含__init__.py文件的一个文件夹,一般还会包含其他模块。
包/模块的命名空间
这里讲一下我对概念“在cookFish的命名空间下”的理解。
Python的importA会把A的Python代码运行一遍,并把运行结果放在一个叫A的命名空间下。
提示:如果A是包,A的Python代码就是文件夹A下的__init__.py中的代码。如果A是模块,那么就是文件A.py中的代码。
importB会把B的Python代码运行一遍,并把运行结果放在一个叫B的命名空间下。假设A和B中都有一个叫X的对象,A中的X在当前命名空间下叫A.X,B中的X在当前命名空间下叫B.X,两个X在当前命名空间下不重名。
提示:这里的对象指Python中的变量/属性,函数,类,实例等等。
比如__version__属性(或者叫它变量)就在cookFish的命名空间下,我们只能通过cookFish.__version__才能访问到__version__,直接输入__version__访问不到它,会报错。
直接输入__version__运行会报如下错误:
>>>__version__ Traceback(mostrecentcalllast): File"",line1,in NameError:name'__version__'isnotdefined
其他导入包/模块的方式
如果我们想导入cookFish下的模块cookBook呢?可以用下面的语法:
>>>importcookFish.cookBook
然后在cookFish的命名空间下又多了cookBook。
>>>dir(cookFish) ['__author__','__builtins__','__cached__','__doc__','__file__','__loader__','__name__','__package__','__path__','__spec__','__version__','cookBook','cookFish_hello']
然后就能通过全名cookFish.cookBook访问cookBook.py中的对象了,比如:
>>>cookFish.cookBook.cookBook_hello() cookBook_hello()fromcookBook.py
好长的名字啊,能不能短一点啊?当然可以:
>>>importcookFish.cookBookascb
然后在当前命名空间下就多了对象cb:
>>>dir() ['__annotations__','__builtins__','__doc__','__loader__','__name__','__package__','__spec__','cb','cookFish','os']
然后就能通过别名cb来访问cookBook.py中的对象了,比如:
>>>cb.cookBook_hello() cookBook_hello()fromcookBook.py
那我能不能只导入cookBook_hello()到当前命名空间?当然可以
>>>fromcookFish.cookBookimportcookBook_hello
然后cookBook_hello就被导入到当前命名空间下了:
>>>dir() ['__annotations__','__builtins__','__doc__','__loader__','__name__','__package__','__spec__','cb','cookBook_hello','cookFish','os']
然后就能直接访问cookBook_hello()了,不用任何前缀:
>>>cookBook_hello() cookBook_hello()fromcookBook.py
“from包/模块名import*”是导入所有对象吗?
那我可以一次性导入cookFish下的所有模块、所有包吗?可以也不可以。
Python有一个条指令
from包/模块名import*
比如fromcookFishimport*,给我们的第一感觉是,这条指令是遍历了cookFish下的所有文件,找到这个包下面的所有包和模块,把他们统统导入到当前命名空间。
但不幸的是,这个操作在windows和Mac系统上不能很好地实现。因为它们的文件系统不能提供准确的文件名大小写信息。在这两个平台上,Python不知道应该把ECHO.py导入为模块echo,Echo还是ECHO,或者其他。(比如windows95上面,所有文件名的首字母都会显示为大写)。如果Python把ECHO.py导入为模块Echo,但实际Python代码中有时按照echo使用的,那肯定会报错。[1]
Python支持大小写,Echo和ECHO是两个不一样的对象
Python的唯一的解决办法是包的作者提供一个明确的包的索引,告诉Python在Python代码中如何命名这个模块。import语句定义下面一个约定,如果在包的__init__.py中定义了一个__all__列表,在fromxxximport*时,Python就会把__all__列表中的对象导入。
!注意:
__all__只对fromxxximport*有影响,对其他import操作没有任何影响
在cookFish/__init__.py中,我们只把函数cookFish_hello加入__all__中,代码如下:
__all__=['cookFish_hello',]#addedtosupport`fromxxximport*` __version__='0.1' __author__='XIEByron' defcookFish_hello(): print("cookFish_Hello()fromcookFish/__init__.py")
重启Python解释器,在导入之前,先运行dir()显示当前命名空间的对象。
>>>dir() ['__annotations__','__builtins__','__doc__','__loader__','__name__','__package__','__spec__','os']
!注意:
Python解释器为了提高运行效率,同一个模块只会导入一次。一个模块被导入后,再次运行导入命名不会重新导入。为了显示fromxximport*的特殊性,所以需要重启Python解释器(就是关闭Python解释器,然后重新进入)。
然后运行如下:
>>>fromcookFishimport*
然后输入dir()查看cookFish_Hello()是否被导入到了当前命名空间.
>>>dir() ['__annotations__','__builtins__','__doc__','__loader__','__name__','__package__','__spec__','cookFish_hello','os']
可以看到只有在__all__列表中的cookFish_hello被导入到当前命名空间,其他什么都没有导入,连cookFish本身也没有被导入。
所以问题“可以一次性导入cookFish下的所有模块、所有包吗?“的答案是:是否能一次导入,取决于包的作者有没有把所有子模块/子包都加入到__all__列表中。
参考
[1]Built-inPackageSupportinPython1.5
版本
[1]version1.0,releasedon2019-04-21
[2]version1.1,releasedon2019-04-21
添加了Python命令的输出。运行工具为windows版本Python(3.7)自带的命令行解释器。