Skip to content

Python 模块

模块(Module)是组织 Python 代码的基本方式。它们允许你将相关的函数、类和变量分组到单独的文件中,从而提高代码的可重用性、可维护性和逻辑结构。模块本质上是一个包含 Python 定义和语句的 Python 文件(.py)。

可以将模块想象成工具箱。你不会把所有工具都分散在各处,而是将相关工具(例如扳手或螺丝刀)分组到特定的工具箱中。当你需要某个特定工具时,你会拿起正确的工具箱(导入模块),然后从中取出你需要的工具。

让我们创建一个名为 helpers.py 的模块:

helpers.py
"""A simple module with helper functions."""
PI = 3.14159
def greet(name):
"""Greets the person with the given name."""
print(f"Hello, {name}!")
class Calculator:
"""A basic calculator class."""
def add(self, x, y):
return x + y

要在另一个 Python 文件(例如 main.py)中使用模块中的定义,可以使用 import 语句。基本语法是:

import module_name
# 或者导入多个模块
import module1, module2
# 或者更推荐的风格(每行一个 import)
import module_one
import module_two

当 Python 遇到 import helpers 时,它会在其搜索路径中查找 helpers.py(或其他模块类型)。找到后,它会执行模块的代码(如果尚未导入),并通过模块名作为前缀来提供其定义。

示例(main.py 与 helpers.py 在同一目录下):

main.py
import helpers
helpers.greet("Alice")
print(f"The value of PI is approximately: {helpers.PI}")
calc = helpers.Calculator()
result = calc.add(5, 3)
print(f"5 + 3 = {result}")

运行 python main.py 会产生:

Hello, Alice!
The value of PI is approximately: 3.14159
5 + 3 = 8

模块在程序执行期间只加载和执行一次,即首次导入时。后续导入同一个模块只会检索已加载的模块对象。

此语句允许将特定名称(函数、类、变量)直接导入到当前脚本的命名空间中,从而无需使用模块前缀。

from module_name import name1
# 或者导入多个名称
from module_name import name1, name2
# 或者导入并重命名
from module_name import name1 as alias_name

示例:

main_from.py
from helpers import greet, PI
from helpers import Calculator as Calc # 重命名
greet("Bob")
print(f"PI: {PI}")
my_calc = Calc()
print(f"10 + 5 = {my_calc.add(10, 5)}")

这样做避免了使用 helpers. 前缀,但将导入的名称直接引入了当前作用域。请注意潜在的名称冲突。

你可以使用以下语句从模块中导入所有公共名称(即不以下划线 _ 开头的名称):

from module_name import *

警告: 在生产代码中通常不建议使用这种做法。它可能将许多名称导入当前命名空间,导致难以确定特定名称的来源,并增加名称冲突的风险。在交互式会话中或者有时在特定模块模式(如 __init__.py)中是可接受的。

当你 import my_module 时,Python 会按以下顺序查找它:

  • 包含输入脚本的目录(如果在交互模式下,则为当前目录)。
  • PYTHONPATH 环境变量中列出的目录(如果已设置)。格式取决于平台(例如,在 Unix/macOS 上以冒号分隔,在 Windows 上以分号分隔)。
  • 安装相关的默认路径(通常在 Python 安装目录的 site-packages 子目录中)。

搜索路径可以通过 sys.path 程序化访问,它是一个字符串列表。

import sys
print(sys.path)

PYTHONPATH 是一个环境变量,你可以设置它来添加 Python 查找模块的额外目录。其语法类似于系统的 PATH 变量。

示例 (Linux/macOS):

Terminal window
export PYTHONPATH="/home/user/my_python_libs:/another/path"

示例 (Windows 命令提示符):

Terminal window
set PYTHONPATH=C:\Users\user\my_python_libs;D:\other_path

虽然 PYTHONPATH 可能有用,但使用虚拟环境(virtual environments)和标准包安装工具(pip)来管理依赖关系是现代推荐的方法。

命名空间(Namespace)是从名称到对象的映射(类似于字典)。Python 使用命名空间来避免名称冲突。

Python 遵循 LEGB 规则进行作用域解析:局部(Local)-> 外部(Enclosing)函数局部 -> 全局(Global) -> 内置(Built-in)。

要在函数内部修改全局变量,必须使用 global 关键字。要在外部函数作用域(在嵌套函数中)修改变量,使用 nonlocal 关键字(Python 3+)。

x = 100 # 全局变量
def outer_func():
y = 20 # 外部(封闭)局部变量
def inner_func():
nonlocal y # 修改外部作用域变量
global x # 声明要修改全局变量
z = 5 # 局部变量
y += 5
x += 1
print(f"Inner: x={x}, y={y}, z={z}")
inner_func()
print(f"Outer: x={x}, y={y}")
outer_func()
print(f"Global: x={x}")

内置函数 dir() 在没有参数调用时,返回当前局部作用域中的名称列表。当以对象(例如模块)作为参数调用时,它返回该对象定义的名称(其属性、方法等)的排序列表。

import math
print(dir(math)) # 列出 math 模块中的名称
my_var = 10
def my_func(): pass
print(dir()) # 列出当前(全局)作用域中的名称

输出可能包含一些特殊名称,如 __name__(模块名称)和 __file__(模块文件路径)。

这些内置函数分别返回表示全局和局部命名空间的字典。

模块通常只加载一次。如果你的主程序正在运行期间修改了模块的源代码文件(在交互式开发或长时间运行的应用程序中很常见),更改不会自动反映。要强制重新加载已导入的模块,请使用 importlib.reload() 函数(注意:reload 在 Python 2 中是内置函数,但在 Python 3 中移至 importlib)。

import importlib
import helpers
# ... 使用 helpers 的代码 ...
# 假设 helpers.py 被修改了
importlib.reload(helpers)
# 现在使用更新版本的 helpers
helpers.greet("Updated User")

请谨慎使用 reload,如果管理不当,它可能导致不一致,尤其是在处理复杂依赖关系或有状态对象时。

包(Package)提供了一种使用“点分模块名”来组织 Python 模块命名空间的方式。包本质上是一个包含 Python 模块以及可能的其他子包的目录。要被识别为常规包(在 Python 3.3 之前,至今仍是常见做法),该目录必须包含一个名为 __init__.py 的文件(该文件可以为空)。

示例结构:

sound/
__init__.py
formats/
__init__.py
wavread.py
aiffread.py
...
effects/
__init__.py
echo.py
surround.py
...
filters/
__init__.py
equalizer.py
...

你可以使用点号表示法从包中导入模块:

import sound.effects.echo
sound.effects.echo.apply_echo(...)
from sound.formats import wavread
wavread.read_wavfile(...)
from sound.filters.equalizer import Equalizer
eq = Equalizer()

__init__.py 文件有几个用途:

示例(sound/effects/__init__.py):

sound/effects/__init__.py
print("正在初始化 sound effects 包...")
# 使 echo 和 surround 可以通过 'from sound.effects import ...' 直接访问
from . import echo
from . import surround
# 控制 'from sound.effects import *' 导入的内容
__all__ = ["echo", "surround"]

有了这个 __init__.py 文件,你可以这样做:

import sound.effects
sound.effects.echo.apply_echo(...) # 通过子模块访问

注意:Python 3.3 引入了隐式命名空间包(Implicit Namespace Packages,PEP 420),允许在没有 __init__.py 文件的情况下创建包,这主要用于将一个大型包拆分到多个目录中。