文章目录
前言
当你退出 Python 解释器并重新进入时,之前定义的函数和变量会丢失。因此,如果你要编写较长的程序,最好使用文本编辑器准备代码,并通过脚本方式将其传递给 Python 解释器来执行,这样你可以避免每次重新定义的麻烦。
随着程序变得更加复杂,可能需要将代码拆分为多个文件,这样不仅有助于维护,还能提高代码的可重用性。比如,如果你有一些常用的函数,可以把它们独立封装,在多个程序中导入使用,而无需重复编写相同的代码。
为了支持这种编程方式,Python 提供了模块的机制:你可以将函数、类和变量等定义放入一个文件中,并在其他模块或脚本中导入使用。通过这种方式,你可以有效组织和复用代码,而不必每次都从头开始编写。模块使得代码结构更加清晰,也为开发大型项目提供了更好的可维护性和扩展性。
一个简单的模块例子
在当前目录下创建一个名为 fibo.py 的文件,文件内容如下:
def fib(n):
"""Write Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n):
"""Return Fibonacci series up to n."""
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
然后可以导入模块 fibo 使用
>>> import fibo
>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
导入模块的方式
基本的导入方式
使用 import 语句导入整个模块。例如:
import fibo
fibo.fib(500)
这种方式导入模块后,你需要通过模块名来访问其中的内容,比如 fibo.fib()。
导入特定函数或变量
如果你只需要模块中的某些函数,可以直接将这些函数导入到当前命名空间中。
from fibo import fib, fib2
fib(500)
这样,你就可以直接调用 fib 和 fib2,而不需要再使用模块名作为前缀。
导入所有内容
你还可以通过 from module import * 导入模块中所有的函数和变量(不包括以 _ 开头的名称)。但这种方式通常不推荐使用,因为它会将所有模块中的名称引入到当前命名空间,可能会与已有的变量发生冲突,导致代码难以维护和阅读。
from fibo import *
fib(500)
使用别名导入模块
如果模块名过长,或者你希望在代码中使用更简短的名称,可以给模块或函数指定别名。
# 方式一
import fibo as fib
fib.fib(500)
# 方式二
from fibo import fib as fibonacci
fibonacci(500)
在同一个 Python 解释器会话中,模块只会被导入一次。如果你修改了模块代码并希望立即看到效果,可以使用 importlib.reload() 来重新加载该模块。
以脚本形式执行模块
当你通过脚本方式 python fibo.py <arguments> 运行一个 Python 模块,要注意 __name__ 的值。但直接脚本运行时,__name__ 的值为 __main__ ;当作为模块被导入时,__name__ 的值为模块名称。因此以下代码在作为模块导入时不会运行:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
模块搜索路径
当导入一个名为 spam 的模块时,Python 解释器首先会查找内置模块中是否有该名称。内置模块的名称可以通过 sys.builtin_module_names 获取。如果没有找到,它接着会在 sys.path 指定的目录列表中查找名为 spam.py 的文件。
sys.path 的初始化来源于以下几个位置:
- 当前脚本所在的目录(如果没有指定文件,则是当前工作目录)。
- PYTHONPATH 环境变量(类似于 shell 中的 PATH 变量,包含一系列目录路径)。
- 默认安装路径(通常包括一个 site-packages 目录,由 site 模块处理)。
“编译” Python 文件
为了加速模块加载,Python 会缓存每个模块的编译版本,并将其存储在 __pycache__ 目录下,文件名格式为 module.version.pyc,其中 version 表示编译文件的格式,一般包括 Python 的版本号。
Python 会根据源文件的修改时间检查编译文件是否过期,自动决定是否需要重新编译,这是一个完全自动化的过程。另外,编译后的模块是平台无关的,这意味着同一个库可以在不同架构的系统之间共享。
包 Packages
在 Python 中,包(Package)是通过使用“点分模块名”来构建模块命名空间的一种方式。例如,模块名 A.B 表示在名为 A 的包中有一个子模块 B。就像使用模块可以避免不同模块之间的全局变量名冲突一样,使用点分模块名可以让多模块包(例如 NumPy 或 Pillow)中的模块避免名称冲突。
假设你想设计一个用于统一处理声音文件和声音数据的模块集合(即一个“包”),以下是可能的包结构(通过文件系统层级表示):
sound/ # 顶层包
__init__.py # 初始化 sound 包
formats/ # 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ # 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ # 滤波器子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
在导入包时,Python 会在 sys.path 中查找包。
__init__.py文件是必需的,用于让 Python 将包含该文件的目录视为包。在最简单的情况下,__init__.py可以是一个空文件,但它也可以执行一些初始化代码,或者设置__all__变量。
完整导入子模块
这样加载了 sound.effects.echo 子模块,必须使用完整的模块名来引用它:
import sound.effects.echo
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
导入子模块并去掉包前缀
这样加载了 echo 子模块,并使其直接可用,无需包的前缀:
from sound.effects import echo
echo.echofilter(input, output, delay=0.7, atten=4)
直接导入函数或变量
这样加载了 echo 子模块,并将其 echofilter() 函数直接导入,可以直接使用:
from sound.effects.echo import echofilter
echofilter(input, output, delay=0.7, atten=4)
从包中导入 *
当用户写下 from sound.effects import * 时,理想情况下我们希望 Python 能自动去文件系统中查找并导入包中的所有子模块。但这样做可能会耗费较长时间,并且导入子模块可能会带来不必要的副作用,特别是当这些副作用只应该在显式导入时发生时。
为了解决这个问题,包的作者需要提供一个显式的包索引。根据约定,如果包的 __init__.py 文件中定义了一个名为 __all__ 的列表,那么当执行 from package import * 时,这个列表中的模块会被导入。这个列表由包的作者负责维护,确保在每次发布新版本时更新。例如,sound/effects/__init__.py 文件可能包含以下代码:
__all__ = ["echo", "surround", "reverse"]
这意味着,执行 from sound.effects import * 时,会导入 sound.effects 包中的三个子模块:echo、surround 和 reverse。
在生产代码中,通常不推荐使用 import *。这种做法容易导致命名空间污染,增加代码的维护难度。
相对导入
当包被结构化为多个子包(例如在 sound 包的示例中),你可以使用绝对导入来引用同级包中的子模块。例如,如果 sound.filters.vocoder 模块需要使用 sound.effects 包中的 echo 模块,它可以通过以下方式导入:
from sound.effects import echo
此外,你还可以使用相对导入,通过 from module import name 的形式进行。相对导入使用前导点来表示涉及的当前包和父包。例如,在 surround 模块中,你可能会写:
from . import echo # 导入当前包中的 echo 模块
from .. import formats # 导入父包中的 formats 模块
from ..filters import equalizer # 从父包中的 filters 子包导入 equalizer 模块
相对导入是基于当前模块的名称来进行的。由于主模块的名称总是
__main__,因此作为 Python 应用主模块的模块必须始终使用绝对导入。
参考
https://docs.python.org/3/tutorial/modules.html
327

被折叠的 条评论
为什么被折叠?



