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

作者:Byron_XIE 时间:2021-07-10 02:52:20 

用实例来说明 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)自带的命令行解释器。

来源:https://www.cnblogs.com/byronxie/p/10745292.html

标签:Python,中包/模块的,`import`,操作
0
投稿

猜你喜欢

  • python之关于数组和列表的区别及说明

    2021-07-29 17:35:31
  • 国外新闻报纸排版布局设计欣赏

    2008-02-25 23:14:00
  • Python Base64编码和解码操作

    2022-10-20 06:26:05
  • Django+Celery实现定时任务的示例

    2023-07-27 19:48:18
  • python读取csv文件示例(python操作csv)

    2023-02-28 23:12:02
  • vue-router之解决addRoutes使用遇到的坑

    2024-04-30 10:19:26
  • asp如何从数据库中删除废旧的电子信箱地址?

    2009-11-15 20:04:00
  • Python读取指定日期邮件的实例

    2023-05-28 17:20:01
  • Keras使用ImageNet上预训练的模型方式

    2021-03-01 10:08:51
  • mysql 5.7.13 winx64安装配置方法图文教程

    2024-01-25 19:43:37
  • python的迭代器与生成器实例详解

    2021-06-21 15:36:41
  • Python通用循环的构造方法实例分析

    2022-03-30 00:03:56
  • Go调度器学习之协作与抢占详解

    2024-05-09 10:08:26
  • 字符编码笔记 ASCII,Unicode和UTF-8

    2023-08-02 23:26:03
  • Pygame代码 制作一个贪吃蛇小游戏

    2022-06-29 03:04:27
  • python Manager 之dict KeyError问题的解决

    2022-12-17 07:38:09
  • Oracle SQL性能优化系列学习一

    2010-07-26 13:14:00
  • 利用Python为女神制作一个专属网站

    2021-07-26 23:16:30
  • PHP高级编程实例:编写守护进程

    2023-10-27 02:03:22
  • Python字符串中删除特定字符的方法

    2023-07-25 18:58:54
  • asp之家 网络编程 m.aspxhome.com