Python 教程 / 函数 / 模块

模块 (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. 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__}'") 
最佳实践
将测试代码放在 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 标准库相同的名称。例如:
- 不要创建 sys.pyos.pymath.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 sys
sys.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 在线代码编辑器
📝 运行结果