Python模块与包

部署运行你感兴趣的模型镜像


前言

当你退出 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 的初始化来源于以下几个位置:

  1. 当前脚本所在的目录(如果没有指定文件,则是当前工作目录)。
  2. PYTHONPATH 环境变量(类似于 shell 中的 PATH 变量,包含一系列目录路径)。
  3. 默认安装路径(通常包括一个 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

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值