Python 中包/模块的 `import` 操作代码


Posted in Python onApril 22, 2019

用实例来说明 import 的作用吧。

创建以下包结构。一个文件夹 cookFish/,下面包含两个文件, __init__.pycookBook.py

为什么取这几个名字呢?假设我想用 Python 去做和鱼相关的菜,这件事情很复杂,所以我给它创建了一个包,名叫cookFish, 既然是包,在它下面必须得创建一个文件__init__.py。烧鱼必备条件之一就是菜谱,所以接着创建了 cookBook.py。这几个文件对我们这次来说就足够了,所以就没有再创建其他文件了。

cookFish/
 __init__.py
 cookBook.py

在cookFish/__init__.py中输入如下代码:

__version__ = '0.1'
__author__ = 'XIE Byron'
def cookFish_hello():
 print("cookFish_Hello() from cookFish/__init__.py")

cookFish/cookBook.py中输入如下代码:

def cookBook_hello():
 print("cookBook_hello() from cookBook.py")

提示:下面的实例都是在 Python 自带的命令行解释器(windows+python 3.7)中运行的结果。如果你在其他环境下运行,比如jupyter notebook,输出会有差异。

"import package-name" 都做了什么?

导入包cookFish。

>>> import cookFish

提示:

如果import时出现错误ModuleNotFoundError,如下:

>>> import cookFish
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'cookFish'

建议先将 Python 的当前工作目录设置为 cookFish 的 父文件夹(就是包含cookFish文件夹的文件夹)。命令如下:

>>> import os
>>> 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 没有被导入。这是因为直接 import cookFish 只运行cookFish文件夹下的 __init__.py 文件,不会运行其他模块,所以cookBook没有被导入。

提示:Python 中的模块指后缀 .py的文件,也叫脚本。包 指包含 __init__.py 文件的一个文件夹,一般还会包含其他模块。

包/模块的命名空间

这里讲一下我对概念“在cookFish的命名空间下”的理解。

Python 的 import A 会把 A 的Python 代码运行一遍,并把运行结果放在一个叫A的命名空间下。

提示: 如果 A 是包,A 的 Python 代码就是 文件夹A下的 __init__.py 中的代码。 如果 A 是模块,那么就是文件 A.py 中的代码。

import B会把 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 (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name '__version__' is not defined

其他导入包/模块的方式

如果我们想导入 cookFish 下的模块 cookBook呢?可以用下面的语法:

>>> import cookFish.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() from cookBook.py

好长的名字啊,能不能短一点啊?当然可以:

>>> import cookFish.cookBook as cb

然后在当前命名空间下就多了对象 cb:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'cb', 'cookFish', 'os']

然后就能通过别名cb来访问cookBook.py中的对象了,比如:

>>> cb.cookBook_hello()
cookBook_hello() from cookBook.py

那我能不能只导入cookBook_hello()到当前命名空间?当然可以

>>> from cookFish.cookBook import cookBook_hello

然后 cookBook_hello 就被导入到当前命名空间下了:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'cb', 'cookBook_hello', 'cookFish', 'os']

然后就能直接访问 cookBook_hello()了,不用任何前缀:

>>> cookBook_hello()
cookBook_hello() from cookBook.py

“from 包/模块名 import *” 是导入所有对象吗?

那我可以一次性导入 cookFish 下的所有模块、所有包吗?可以也不可以。

Python 有一个条指令

from 包/模块名 import *

比如from cookFish import *,给我们的第一感觉是,这条指令是遍历了 cookFish 下的所有文件,找到这个包下面的所有包和模块,把他们统统导入到当前命名空间。

但不幸的是,这个操作在windows和Mac系统上不能很好地实现。因为它们的文件系统不能提供准确的文件名大小写信息。在这两个平台上,Python 不知道应该把ECHO.py导入为模块echo, Echo 还是ECHO,或者其他。(比如windows 95 上面,所有文件名的首字母都会显示为大写)。如果Python 把 ECHO.py导入为 模块Echo,但实际Python代码中有时按照 echo 使用的,那肯定会报错。[1]

Python 支持大小写,Echo和ECHO是两个不一样的对象

Python 的唯一的解决办法是包的作者提供一个明确的包的索引,告诉 Python 在 Python 代码中如何命名这个模块。import 语句定义下面一个约定,如果在包的 __init__.py 中定义了一个 __all__ 列表,在 from xxx import * 时,Python 就会把 __all__ 列表中的对象导入。

! 注意:

__all__ 只对 from xxx import * 有影响,对其他 import 操作没有任何影响

在cookFish/__init__.py中, 我们只把函数 cookFish_hello加入__all__ 中,代码如下:

__all__ = ['cookFish_hello', ] # added to support `from xxx import *`
__version__ = '0.1'
__author__ = 'XIE Byron'

def cookFish_hello():
  print("cookFish_Hello() from cookFish/__init__.py")

重启 Python 解释器,在导入之前,先运行 dir()显示当前命名空间的对象。

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'os']

! 注意:

Python 解释器为了提高运行效率,同一个模块只会导入一次。一个模块被导入后,再次运行导入命名不会重新导入。为了显示from xx import * 的特殊性,所以需要重启 Python 解释器(就是关闭 Python 解释器,然后重新进入)。

然后运行如下:

>>> from cookFish import *

然后输入 dir() 查看 cookFish_Hello()是否被导入到了当前命名空间.

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'cookFish_hello', 'os']

可以看到只有在__all__列表中的 cookFish_hello被导入到当前命名空间,其他什么都没有导入,连cookFish本身也没有被导入。

所以问题“可以一次性导入 cookFish 下的所有模块、所有包吗?“ 的答案是:是否能一次导入,取决于包的作者有没有把所有子模块/子包都加入到 __all__列表中。

参考

[1] Built-in Package Support in Python 1.5

版本

[1] version 1.0, released on 2019-04-21

[2] version 1.1, released on 2019-04-21

添加了 Python 命令的输出。运行工具为windows版本Python(3.7)自带的命令行解释器。

Python 相关文章推荐
Python制作简易注册登录系统
Dec 15 Python
Python使用asyncio包处理并发详解
Sep 09 Python
Python抓取框架Scrapy爬虫入门:页面提取
Dec 01 Python
python中多个装饰器的执行顺序详解
Oct 08 Python
Python解决线性代数问题之矩阵的初等变换方法
Dec 12 Python
解决pyinstaller打包pyqt5的问题
Jan 08 Python
基于Python获取城市近7天天气预报
Nov 26 Python
Python 解析pymysql模块操作数据库的方法
Feb 18 Python
python3光学字符识别模块tesserocr与pytesseract的使用详解
Feb 26 Python
Kears 使用:通过回调函数保存最佳准确率下的模型操作
Jun 17 Python
python实现图片转换成素描和漫画格式
Aug 19 Python
深入浅析python3 依赖倒置原则(示例代码)
Jul 09 Python
python定时检测无响应进程并重启的实例代码
Apr 22 #Python
django query模块
Apr 20 #Python
不到20行代码用Python做一个智能聊天机器人
Apr 19 #Python
详解Python3 基本数据类型
Apr 19 #Python
python面向对象法实现图书管理系统
Apr 19 #Python
python远程连接MySQL数据库
Apr 19 #Python
详解Python匿名函数(lambda函数)
Apr 19 #Python
You might like
php for 循环语句使用方法详细说明
2010/05/09 PHP
php登录超时检测功能实例详解
2017/03/21 PHP
php实现根据身份证获取精准年龄
2020/02/26 PHP
禁止JQuery中的load方法装载IE缓存中文件的方法
2009/09/11 Javascript
jquery下checked取值问题的解决方法
2012/08/09 Javascript
浅谈JavaScript函数参数的可修改性问题
2013/12/05 Javascript
用JavaScript判断CSS浏览器类型前缀的两种方法
2015/10/08 Javascript
javascript阻止事件冒泡和浏览器的默认行为
2017/01/21 Javascript
d3.js实现立体柱图的方法详解
2017/04/28 Javascript
vue项目使用axios发送请求让ajax请求头部携带cookie的方法
2018/09/26 Javascript
Vue唯一可以更改vuex实例中state数据状态的属性对象Mutation的讲解
2019/01/18 Javascript
使用apifm-wxapi快速开发小程序过程详解
2019/08/05 Javascript
使用layui 的layedit定义自己的toolbar方法
2019/09/18 Javascript
Webpack设置环境变量的一些误区详解
2019/12/19 Javascript
你可能从未使用过的11+个JavaScript特性(小结)
2020/01/08 Javascript
React.js组件实现拖拽排序组件功能过程解析
2020/04/27 Javascript
python中使用OpenCV进行人脸检测的例子
2014/04/18 Python
对python打乱数据集中X,y标签对的方法详解
2018/12/14 Python
利用Django提供的ModelForm增删改数据的方法
2019/01/06 Python
Pytorch中的VGG实现修改最后一层FC
2020/01/15 Python
Python3连接Mysql8.0遇到的问题及处理步骤
2020/02/17 Python
Python安装OpenCV的示例代码
2020/03/05 Python
浅谈python opencv对图像颜色通道进行加减操作溢出
2020/06/03 Python
Python Pillow(PIL)库的用法详解
2020/09/19 Python
日本整理专家Marie Kondo的官方在线商店:KonMari
2020/06/29 全球购物
如何写一封打动人心的求职信
2014/02/17 职场文书
解除财产保全担保书
2014/05/20 职场文书
2014年度安全生产目标管理责任书
2014/07/25 职场文书
委托证明书
2014/09/17 职场文书
团组织推优材料
2014/12/29 职场文书
机关单位保密工作责任书
2015/05/11 职场文书
法定代表人资格证明书
2015/06/18 职场文书
初中运动会前导词
2015/07/20 职场文书
基于tensorflow权重文件的解读
2021/05/26 Python
html输入两个数实现加减乘除功能
2021/07/01 HTML / CSS
纯CSS3实现div按照顺序出入效果
2021/07/15 HTML / CSS