模块 (Module)
模块是Python程序架构的核心概念。想象一下,如果把所有代码都写在一个文件里,代码会变得难以维护。Python允许我们将代码分散到多个文件,每个文件就是一个模块,通过导入机制将它们组织起来。
什么是模块?
在Python中,一个 .py 文件就是一个模块(Module)。模块可以包含函数、类、变量、以及可执行代码。
┌─────────────────────────────────────────────────────────────────┐
│ Python 项目结构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ myproject/ ← 项目根目录 │
│ ├── main.py ← 主程序入口 │
│ ├── utils.py ← 工具模块 │
│ ├── models.py ← 数据模型模块 │
│ ├── config.py ← 配置模块 │
│ └── database/ ← database 包 │
│ ├── __init__.py ← 包初始化文件 │
│ ├── connection.py ← 连接管理模块 │
│ └── queries.py ← 查询模块 │
│ │
│ 导入关系: │
│ main.py → import utils, models, config │
│ models.py → from database import connection, queries │
│ │
└─────────────────────────────────────────────────────────────────┘
模块的优点
1. 代码复用 - 一次编写,多次使用
2. 易于维护 - 修改一个模块不影响其他部分
3. 命名空间隔离 - 避免变量名冲突
4. 团队协作 - 多人可以同时开发不同模块
1. 代码复用 - 一次编写,多次使用
2. 易于维护 - 修改一个模块不影响其他部分
3. 命名空间隔离 - 避免变量名冲突
4. 团队协作 - 多人可以同时开发不同模块
导入语法详解
1. import 语句
Python
# 语法: import 模块名
# 导入整个模块,通过 模块名.成员 访问
import math
print(math.pi) # 3.141592653589793
print(math.sqrt(16)) # 4.0
print(math.pow(2, 3)) # 8.0
# 导入多个模块
import os
import sys
import datetime
print(os.getcwd())
print(sys.version)
2. from...import 语句
Python
# 语法: from 模块名 import 成员
# 直接导入特定成员,无需加模块名前缀
from math import sqrt, pi
print(pi) # 直接使用,无需 math.pi
print(sqrt(16)) # 直接使用,无需 math.sqrt
# 导入所有公开成员 (不推荐)
from math import *
# 给导入的成员起别名
from math import sqrt as square_root
print(square_root(9)) # 3.0
3. import...as 语句
Python
# 给模块起别名,简化长模块名
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
np.array([1, 2, 3])
pd.DataFrame({"a": [1, 2]})
plt.plot([1, 2, 3])
# 同时给模块和成员起别名
import math as m
from math import pi as PI
| 导入方式 | 语法 | 优点 | 缺点 |
|---|---|---|---|
| import 模块 | math.sqrt(16) |
清晰知道成员来源 | 代码较冗长 |
| from 模块 import 成员 | sqrt(16) |
简洁 | 来源不明确,可能冲突 |
| import 模块 as 别名 | np.sqrt(16) |
简洁且避免冲突 | 需要记忆别名 |
| from 模块 import * | sqrt(16) |
最简洁 | 命名空间污染,不推荐 |
创建和使用模块
1. 创建自己的模块
创建一个 Python 文件,就是创建了一个模块。让我用实际项目结构来演示:
┌─────────────────────────────────────────────────────────────────┐
│ calculator/ 项目目录 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ calculator/ │
│ ├── __init__.py ← 使目录成为包 (可为空) │
│ ├── basic.py ← 基础运算模块 │
│ ├── advanced.py ← 高级运算模块 │
│ └── main.py ← 主程序 │
│ │
└─────────────────────────────────────────────────────────────────┘
basic.py
"""基础运算模块 - 提供加减乘除基本运算"""
# 公开接口 (Public) - 允许被外部访问
PI = 3.14159
def add(a, b):
"""加法运算"""
return a + b
def subtract(a, b):
"""减法运算"""
return a - b
def multiply(a, b):
"""乘法运算"""
return a * b
def divide(a, b):
"""除法运算"""
if b == 0:
raise ValueError("除数不能为零")
return a / b
# 内部实现 (Private) - 不应被外部直接访问
def _validate_number(n):
"""验证是否为有效数字"""
return isinstance(n, (int, float))
advanced.py
"""高级运算模块 - 提供指数、对数、三角函数等高级运算"""
import math
def power(base, exponent):
"""幂运算"""
return math.pow(base, exponent)
def square_root(n):
"""平方根"""
return math.sqrt(n)
def factorial(n):
"""阶乘"""
if n < 0:
raise ValueError("负数没有阶乘")
if n <= 1:
return 1
return n * factorial(n - 1)
main.py
# 主程序 - 使用自定义模块
# 方式1: 导入整个模块
import basic
import advanced
print(basic.add(10, 5)) # 15
print(basic.multiply(3, 4)) # 12
print(advanced.square_root(16)) # 4.0
# 方式2: 从模块导入特定成员
from basic import add, multiply
from advanced import power
print(add(100, 200)) # 300
print(power(2, 10)) # 1024.0
# 方式3: 给模块起别名
import basic as b
import advanced as adv
print(b.PI)
2. 模块的标准模板
Python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
模块名称: mymodule
模块描述: 这个模块做什么的详细说明
作者: Your Name
版本: 1.0.0
日期: 2026-01-01
"""
# 公开接口
__all__ = ['public_function', 'PublicClass']
# 模块级变量
VERSION = '1.0.0'
# 公开函数
def public_function():
pass
# 公开类
class PublicClass:
pass
# 私有函数 (不应被外部直接访问)
def _private_function():
pass
# 测试代码
if __name__ == '__main__':
# 只有直接运行此文件时才会执行
public_function()
print("模块测试通过!")
__name__ 属性
__name__ 是 Python 模块的一个特殊属性,它告诉我们模块是如何被使用的:
┌─────────────────────────────────────────────────────────────────┐
│ __name__ 属性详解 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 场景1: 直接运行模块 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ $ python mymodule.py │ │
│ │ │ │
│ │ __name__ == "__main__" → True │ │
│ │ │ │
│ │ if __name__ == "__main__": │ │
│ │ # 这段代码会被执行 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 场景2: 作为模块被导入 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ >>> import mymodule │ │
│ │ │ │
│ │ __name__ == "mymodule" → True │ │
│ │ │ │
│ │ if __name__ == "__main__": │ │
│ │ # 这段代码不会执行 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Python
def add(a, b):
return a + b
def multiply(a, b):
return a * b
# 测试代码 - 只有直接运行此文件时才会执行
if __name__ == '__main__':
print("直接运行模式")
print("add(3, 5) =", add(3, 5))
print("multiply(4, 6) =", multiply(4, 6))
# 验证 __name__ 的值
print(f"当前模块的 __name__ = '{__name__}'")
最佳实践
将测试代码放在
1. 直接运行模块时可以测试功能
2. 被其他模块导入时不会执行测试代码
将测试代码放在
if __name__ == '__main__': 块中,这样:1. 直接运行模块时可以测试功能
2. 被其他模块导入时不会执行测试代码
作用域与命名空间
Python 模块中有三种作用域的命名规则:
┌─────────────────────────────────────────────────────────────────┐
│ 命名空间作用域规则 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 公开 (Public) │ │
│ │ 规则: 普通名称 │ │
│ │ 示例: PI, add, MyClass, my_variable │ │
│ │ 用途: 可以被其他模块直接导入和使用 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 受保护 (Protected) │ │
│ │ 规则: 单下划线前缀 _ │ │
│ │ 示例: _private_func, _internal_data │ │
│ │ 用途: 提示这是内部实现,不应在外部使用 │ │
│ │ 注意: from module import * 不会导入以 _ 开头的成员 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 特殊 (Special/Python 内置) │ │
│ │ 规则: 双下划线前缀和后缀 __xx__ │ │
│ │ 示例: __name__, __doc__, __init__, __all__ │ │
│ │ 用途: Python 特殊属性和方法,有特定含义 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 私有 (Private) │ │
│ │ 规则: 双下划线前缀 __xx │ │
│ │ 示例: __private_method, __secret_data │ │
│ │ 用途: 名称会被改编 (Name Mangling),难以直接访问 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
使用 __all__ 控制导出
Python
# mymodule.py
def public_func():
return "public"
def _private_func():
return "private"
def helper_func():
return "helper"
# 定义公开接口 - from mymodule import * 只导入这些
__all__ = ['public_func', 'HelperClass']
class HelperClass:
pass
Python
# 测试 __all__ 的作用
# 方式1: from module import *
# 只会导入 __all__ 中列出的成员
# public_func 和 HelperClass 会被导入
# _private_func 和 helper_func 不会被导入
# 方式2: 显式导入
# 可以导入任何成员,包括 _private_func
# from mymodule import _private_func # 可以工作,但不推荐
# 查看模块的公开成员
import mymodule
print(f"__all__ = {mymodule.__all__}")
print(f"所有成员: {dir(mymodule)}")
包 (Package)
当模块越来越多时,我们需要用包来组织模块。包就是包含 __init__.py 文件的目录。
┌─────────────────────────────────────────────────────────────────┐
│ 包结构示例 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ mycompany/ ← 顶级包 (避免与标准库冲突) │
│ ├── __init__.py ← 包初始化文件 │
│ ├── admin.py ← admin 模块 │
│ ├── utils.py ← utils 模块 │
│ │ │
│ ├── web/ ← web 子包 │
│ │ ├── __init__.py ← 子包初始化文件 │
│ │ ├── views.py ← views 模块 │
│ │ ├── urls.py ← urls 模块 │
│ │ └── static/ ← static 子包 │
│ │ ├── __init__.py │
│ │ └── assets.py │
│ │ │
│ └── api/ ← api 子包 │
│ ├── __init__.py │
│ ├── v1/ ← v1 子包 │
│ │ ├── __init__.py │
│ │ └── endpoints.py │
│ └── v2/ ← v2 子包 │
│ ├── __init__.py │
│ └── endpoints.py │
│ │
└─────────────────────────────────────────────────────────────────┘
包的导入方式
Python
# 假设有如下包结构:
# mycompany/
# __init__.py
# web/
# __init__.py
# views.py
# api/
# __init__.py
# v1/
# __init__.py
# endpoints.py
# 导入整个包
import mycompany
import mycompany.web
# 导入特定模块
import mycompany.web.views
from mycompany.api.v1 import endpoints
# 从模块导入特定成员
from mycompany.web.views import home, about
# 使用别名简化
import mycompany.web.views as web_views
from mycompany.api.v1.endpoints import get_user as v1_get_user
__init__.py 的作用
Python
# __init__.py 可以:
# 1. 初始化包级别的变量
# mycompany/__init__.py
VERSION = '1.0.0'
AUTHOR = 'Your Name'
# 2. 批量导入模块,方便外部调用
# mycompany/web/__init__.py
from . import views
from . import urls
# 这样可以: from mycompany.web import views, urls
# 3. 设置 __all__ 控制 from package import * 的行为
# __all__ = ['views', 'urls']
命名冲突警告
创建自己的模块时,不要使用与 Python 标准库相同的名称。例如:
- 不要创建
- 使用包来组织自定义模块,如
- 导入前可以先测试:
创建自己的模块时,不要使用与 Python 标准库相同的名称。例如:
- 不要创建
sys.py、os.py、math.py- 使用包来组织自定义模块,如
mycompany.sys- 导入前可以先测试:
import abc,成功则说明已存在
模块搜索路径
当你 import 一个模块时,Python 按照以下顺序搜索:
┌─────────────────────────────────────────────────────────────────┐
│ 模块搜索路径顺序 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 搜索顺序 (从 1 到 4): │
│ │
│ 1. 内置模块 (Built-in Modules) │
│ └─ Python 解释器自带的模块 │
│ 示例: sys, os, math, random, json │
│ │
│ 2. 当前目录 │
│ └─ 脚本所在目录或当前工作目录 │
│ │
│ 3. PYTHONPATH 环境变量指定的目录 │
│ └─ 可以通过设置此变量添加自定义路径 │
│ │
│ 4. Python 安装目录中的 site-packages │
│ └─ 第三方包安装位置 │
│ 示例: /usr/lib/python3/site-packages/ │
│ ~/.local/lib/python3.x/site-packages/ │
│ │
│ 注意: 搜索按顺序进行,找到第一个匹配的模块即停止 │
│ │
└─────────────────────────────────────────────────────────────────┘
Python
# 查看模块搜索路径
import sys
print("Python 模块搜索路径:")
for i, path in enumerate(sys.path, 1):
print(f" {i}. {path}")
# sys.path 是一个列表,可以动态修改
# 添加自定义搜索路径
sys.path.append('/path/to/my/modules')
sys.path.insert(0, '/path/to/priority/modules')
# 查看已加载的模块
print(f"\\n已加载的模块数量: {len(sys.modules)}")
动态添加搜索路径
在代码中添加自定义路径:
注意:建议在导入任何模块之前就添加,否则可能导入错误版本的同名模块。
在代码中添加自定义路径:
import syssys.path.insert(0, '/your/custom/path')注意:建议在导入任何模块之前就添加,否则可能导入错误版本的同名模块。
相对导入与绝对导入
┌─────────────────────────────────────────────────────────────────┐
│ 包内导入方式对比 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 假设文件结构: │
│ mypackage/ │
│ ├── __init__.py │
│ ├── module_a.py │
│ └── subpackage/ │
│ ├── __init__.py │
│ └── module_b.py │
│ │
│ 在 module_b.py 中导入 module_a.py: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 绝对导入 (Absolute Import) - 推荐 │ │
│ │ │ │
│ │ from mypackage import module_a │ │
│ │ from mypackage.module_a import some_function │ │
│ │ │ │
│ │ 优点: 清晰明确,不受当前包位置影响 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 相对导入 (Relative Import) │ │
│ │ │ │
│ │ from . import module_a ← 同级目录 │ │
│ │ from .. import module_a ← 上一级目录 │ │
│ │ from ..module_a import xxx │ │
│ │ │ │
│ │ 优点: 简短,包重命名时修改少 │ │
│ │ 缺点: 只能在包内使用,直接运行会失败 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Python
# 绝对导入示例 (推荐)
# 在 mypackage/subpackage/module_b.py 中:
# 导入同包内的模块
from mypackage import module_a
# 导入同包内的特定函数
from mypackage.module_a import some_function
# 导入标准库或其他第三方包
import json
import requests
Python
# 相对导入示例
# 在 mypackage/subpackage/module_b.py 中:
# . 表示当前包
from . import module_c # 导入同级的 module_c
from .module_c import xxx # 导入模块中的特定成员
# .. 表示上级包
from .. import module_a # 导入上级包中的 module_a
# ... 表示上上级包 (以此类推)
from ... import some_module # 导入上上级包中的模块
# 注意: 相对导入不能用于直接运行的脚本!
# python module_b.py # 会报错: attempted relative import with no known parent package
知识测验
问题1:以下代码的输出是什么?
# 在 test_module.py 中
__author__ = "Alice"
def hello():
return "Hello!"
if __name__ == '__main__':
print(__author__)
# 问题: 直接运行 python test_module.py,输出是什么?
问题2:以下哪种导入方式会将所有公开成员导入到当前命名空间?
问题3:假设有目录结构 mypkg/__init__.py 和 mypkg/utils.py,以下哪个导入语句是正确的?
课后练习
练习1:创建计算器模块
创建一个模块结构,实现一个完整的计算器:
- 创建
calculator/目录作为包 calculator/__init__.py- 设置版本号和导出接口calculator/basic_ops.py- 实现加减乘除calculator/advanced_ops.py- 实现指数、对数、阶乘- 在
__init__.py中使用from .basic_ops import *导出 - 添加
if __name__ == '__main__':测试代码
练习2:理解作用域规则
创建一个模块,演示各种作用域:
- 创建公开变量
PUBLIC_VAR - 创建受保护变量
_PROTECTED_VAR - 创建私有变量
__PRIVATE_VAR - 使用
dir()查看模块的所有属性 - 使用
from module import *测试哪些变量会被导入
练习3:包结构设计
为一个博客系统设计包结构:
blog/- 顶级包blog/models/- 数据模型子包(User, Article, Comment)blog/views/- 视图子包(home, post, user)blog/utils/- 工具子包(date, text, email)- 每个子包有
__init__.py导出常用类 - 在主包
__init__.py中导入所有子包的公开接口
章节总结
┌─────────────────────────────────────────────────────────────────┐
│ 本章要点 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 模块基础 │
│ • 每个 .py 文件是一个模块 │
│ • 模块包含函数、类、变量和可执行代码 │
│ │
│ 2. 导入方式 │
│ • import 模块名 │
│ • from 模块名 import 成员 │
│ • import 模块名 as 别名 │
│ • from 模块名 import * (不推荐) │
│ │
│ 3. __name__ 属性 │
│ • 直接运行时: __name__ == "__main__" │
│ • 被导入时: __name__ == "模块名" │
│ │
│ 4. 作用域规则 │
│ • 公开: 普通名称 │
│ • 受保护: _前缀 │
│ • 特殊: __前缀__后缀 (如 __init__) │
│ • 私有: __前缀 (名称会被改编) │
│ │
│ 5. 包结构 │
│ • 包是包含 __init__.py 的目录 │
│ • 包可以嵌套,形成多级层次结构 │
│ │
│ 6. 搜索路径 │
│ • 内置模块 → 当前目录 → PYTHONPATH → site-packages │
│ • sys.path 列表可以动态修改 │
│ │
└─────────────────────────────────────────────────────────────────┘
🐍 Python 在线代码编辑器
📝 运行结果