Skip to content

Python 模块和包

在 Python 中,模块和包是组织代码的重要方式。本章节将详细介绍 Python 中的模块和包的概念、创建方法和使用技巧。

模块(Module)

模块是一个包含 Python 定义和语句的文件,文件名就是模块名加上 .py 后缀。

基本概念

  • 模块:一个 .py 文件就是一个模块
  • 模块名:文件名去掉 .py 后缀
  • 命名空间:每个模块都有自己的命名空间,避免命名冲突

创建模块

创建一个简单的模块 my_module.py

python
# my_module.py

# 变量
pi = 3.14159
name = "My Module"

# 函数
def add(a, b):
    """计算两个数的和"""
    return a + b

def subtract(a, b):
    """计算两个数的差"""
    return a - b

# 类
class Calculator:
    """简单的计算器类"""
    
    def multiply(self, a, b):
        """计算两个数的乘积"""
        return a * b
    
    def divide(self, a, b):
        """计算两个数的商"""
        if b == 0:
            raise ValueError("除数不能为零")
        return a / b

# 执行代码(只有在直接运行模块时才会执行)
if __name__ == "__main__":
    print("直接运行模块")
    print(f"pi = {pi}")
    print(f"add(1, 2) = {add(1, 2)}")
    calc = Calculator()
    print(f"calc.multiply(3, 4) = {calc.multiply(3, 4)}")

导入模块

有多种方式导入模块:

1. 导入整个模块

python
# 导入整个模块
import my_module

# 使用模块中的变量
print(f"pi = {my_module.pi}")

# 使用模块中的函数
print(f"add(1, 2) = {my_module.add(1, 2)}")

# 使用模块中的类
calc = my_module.Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")

2. 导入模块中的特定内容

python
# 导入模块中的特定内容
from my_module import pi, add, Calculator

# 使用导入的变量
print(f"pi = {pi}")

# 使用导入的函数
print(f"add(1, 2) = {add(1, 2)}")

# 使用导入的类
calc = Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")

3. 导入模块中的所有内容

python
# 导入模块中的所有内容
from my_module import *

# 使用导入的变量
print(f"pi = {pi}")
print(f"name = {name}")

# 使用导入的函数
print(f"add(1, 2) = {add(1, 2)}")
print(f"subtract(5, 3) = {subtract(5, 3)}")

# 使用导入的类
calc = Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")
print(f"divide(10, 2) = {calc.divide(10, 2)}")

4. 导入模块并使用别名

python
# 导入模块并使用别名
import my_module as mm

# 使用别名访问模块内容
print(f"pi = {mm.pi}")
print(f"add(1, 2) = {mm.add(1, 2)}")
calc = mm.Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")

# 导入特定内容并使用别名
from my_module import add as addition, subtract as subtraction

print(f"addition(1, 2) = {addition(1, 2)}")
print(f"subtraction(5, 3) = {subtraction(5, 3)}")

模块的搜索路径

当导入一个模块时,Python 会按照以下顺序搜索模块:

  1. 当前目录
  2. 环境变量 PYTHONPATH 中的目录
  3. Python 标准库目录
  4. 任何 .pth 文件中指定的目录

可以通过 sys.path 查看搜索路径:

python
# 查看模块搜索路径
import sys
print("模块搜索路径:")
for path in sys.path:
    print(path)

标准库模块

Python 内置了很多标准库模块,这些模块提供了各种功能:

模块名功能描述
os操作系统接口
sysPython 解释器相关
math数学函数
random随机数生成
datetime日期和时间处理
jsonJSON 数据处理
re正则表达式
collections高级数据结构
itertools迭代器工具
functools函数工具

使用示例

python
# 使用标准库模块

# os 模块
import os
print(f"当前目录:{os.getcwd()}")
print(f"当前操作系统:{os.name}")

# sys 模块
import sys
print(f"Python 版本:{sys.version}")
print(f"命令行参数:{sys.argv}")

# math 模块
import math
print(f"π = {math.pi}")
print(f"sin(π/2) = {math.sin(math.pi/2)}")
print(f"平方根(16) = {math.sqrt(16)}")

# random 模块
import random
print(f"随机整数(1-10):{random.randint(1, 10)}")
print(f"随机浮点数:{random.random()}")
print(f"随机选择:{random.choice(['apple', 'banana', 'cherry'])}")

# datetime 模块
import datetime
now = datetime.datetime.now()
print(f"当前时间:{now}")
print(f"年份:{now.year}")
print(f"月份:{now.month}")
print(f"日期:{now.day}")

# json 模块
import json
data = {"name": "Alice", "age": 30, "city": "New York"}
json_str = json.dumps(data)
print(f"JSON 字符串:{json_str}")
parsed_data = json.loads(json_str)
print(f"解析后的数据:{parsed_data}")

# re 模块
import re
pattern = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
email = "example@example.com"
if re.match(pattern, email):
    print(f"{email} 是有效的邮箱地址")
else:
    print(f"{email} 不是有效的邮箱地址")

包(Package)

包是一个包含多个模块的目录,它必须包含一个 __init__.py 文件。

创建包

创建一个包的步骤:

  1. 创建一个目录作为包名
  2. 在该目录中创建一个 __init__.py 文件
  3. 在该目录中创建多个模块文件

示例包结构

my_package/
├── __init__.py
├── module1.py
├── module2.py
└── subpackage/
    ├── __init__.py
    └── module3.py

__init__.py 文件

__init__.py 文件可以为空,也可以包含包的初始化代码。

示例 __init__.py 文件:

python
# my_package/__init__.py

# 包的版本
__version__ = "1.0.0"

# 包的描述
__description__ = "我的第一个 Python 包"

# 导入模块中的内容
from .module1 import add, subtract
from .module2 import multiply, divide

# 定义 __all__ 变量,控制 from my_package import * 导入的内容
__all__ = ['add', 'subtract', 'multiply', 'divide']

模块文件

module1.py

python
# my_package/module1.py

def add(a, b):
    """计算两个数的和"""
    return a + b

def subtract(a, b):
    """计算两个数的差"""
    return a - b

module2.py

python
# my_package/module2.py

def multiply(a, b):
    """计算两个数的乘积"""
    return a * b

def divide(a, b):
    """计算两个数的商"""
    if b == 0:
        raise ValueError("除数不能为零")
    return a / b

subpackage/__init__.py

python
# my_package/subpackage/__init__.py

# 导入子包中的模块
from .module3 import power

__all__ = ['power']

subpackage/module3.py

python
# my_package/subpackage/module3.py

def power(base, exponent):
    """计算幂"""
    return base ** exponent

导入包

有多种方式导入包:

1. 导入整个包

python
# 导入整个包
import my_package

# 使用包中的变量
print(f"包版本:{my_package.__version__}")
print(f"包描述:{my_package.__description__}")

# 使用包中的函数
print(f"add(1, 2) = {my_package.add(1, 2)}")
print(f"multiply(3, 4) = {my_package.multiply(3, 4)}")

2. 导入包中的模块

python
# 导入包中的模块
import my_package.module1
import my_package.module2

# 使用模块中的函数
print(f"add(1, 2) = {my_package.module1.add(1, 2)}")
print(f"subtract(5, 3) = {my_package.module1.subtract(5, 3)}")
print(f"multiply(3, 4) = {my_package.module2.multiply(3, 4)}")
print(f"divide(10, 2) = {my_package.module2.divide(10, 2)}")

3. 导入包中的特定内容

python
# 导入包中的特定内容
from my_package import add, multiply
from my_package.module1 import subtract
from my_package.module2 import divide

# 使用导入的函数
print(f"add(1, 2) = {add(1, 2)}")
print(f"subtract(5, 3) = {subtract(5, 3)}")
print(f"multiply(3, 4) = {multiply(3, 4)}")
print(f"divide(10, 2) = {divide(10, 2)}")

4. 导入子包

python
# 导入子包
import my_package.subpackage
from my_package.subpackage import power

# 使用子包中的函数
print(f"power(2, 3) = {my_package.subpackage.power(2, 3)}")
print(f"power(5, 2) = {power(5, 2)}")

5. 使用别名

python
# 使用别名
import my_package as mp
import my_package.module1 as m1
import my_package.subpackage as sp

# 使用别名访问
print(f"add(1, 2) = {mp.add(1, 2)}")
print(f"subtract(5, 3) = {m1.subtract(5, 3)}")
print(f"power(2, 3) = {sp.power(2, 3)}")

相对导入

在包内部,可以使用相对导入来导入同一包中的其他模块。

相对导入的符号:

  • .:当前目录
  • ..:父目录
  • ...:祖父目录,以此类推

示例

python
# my_package/module4.py

# 相对导入
from .module1 import add
from .module2 import multiply
from .subpackage.module3 import power

# 使用导入的函数
def calculate(a, b):
    """计算 a + b * 2^3"""
    return add(a, multiply(b, power(2, 3)))

if __name__ == "__main__":
    print(f"calculate(1, 2) = {calculate(1, 2)}")

模块和包的最佳实践

1. 模块命名规范

  • 模块名应该使用小写字母,单词之间用下划线分隔
  • 模块名应该简洁明了,反映模块的功能
  • 避免使用与标准库模块同名的模块名

2. 包命名规范

  • 包名应该使用小写字母,单词之间用下划线分隔
  • 包名应该简洁明了,反映包的功能
  • 避免使用与标准库包同名的包名

3. __init__.py 文件

  • __init__.py 文件应该保持简洁
  • 可以在 __init__.py 文件中定义包的版本、作者等元数据
  • 可以在 __init__.py 文件中导入常用的模块内容,方便用户使用
  • 可以定义 __all__ 变量,控制 from package import * 导入的内容

4. 导入方式

  • 对于标准库模块,应该使用 import modulefrom module import something
  • 对于第三方库,应该使用 import modulefrom module import something
  • 对于自己的模块,应该使用相对导入(在包内部)或绝对导入
  • 避免使用 from module import *,除非是在交互式环境中

5. 模块和包的组织

  • 按照功能组织模块和包
  • 一个模块应该只包含相关的功能
  • 对于大型项目,可以创建多个包和子包
  • 使用 setup.pypyproject.toml 文件来管理项目

模块的加载和缓存

当导入一个模块时,Python 会:

  1. 搜索模块文件
  2. 编译模块文件(如果需要)
  3. 执行模块代码
  4. 创建模块对象
  5. 将模块对象缓存到 sys.modules

这样,当再次导入同一模块时,Python 会直接从缓存中获取,而不会重新执行模块代码。

查看已加载的模块

python
# 查看已加载的模块
import sys

print("已加载的模块:")
for module_name in list(sys.modules.keys())[:20]:  # 只显示前 20 个
    print(module_name)

print(f"\n已加载模块数量:{len(sys.modules)}")

重新加载模块

如果修改了模块代码,可以使用 importlib.reload() 重新加载模块:

python
# 重新加载模块
import my_module
import importlib

# 第一次导入
print(f"第一次导入:pi = {my_module.pi}")

# 修改模块代码后,重新加载
importlib.reload(my_module)
print(f"重新加载后:pi = {my_module.pi}")

示例:创建一个完整的包

项目结构

calculator/
├── __init__.py
├── arithmetic.py
├── geometry.py
└── utils/
    ├── __init__.py
    └── conversion.py

文件内容

calculator/__init__.py

python
# calculator/__init__.py

__version__ = "1.0.0"
__author__ = "Your Name"

from .arithmetic import add, subtract, multiply, divide
from .geometry import area_of_circle, area_of_rectangle
from .utils import convert_to_float

__all__ = [
    'add', 'subtract', 'multiply', 'divide',
    'area_of_circle', 'area_of_rectangle',
    'convert_to_float'
]

calculator/arithmetic.py

python
# calculator/arithmetic.py

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

calculator/geometry.py

python
# calculator/geometry.py

import math

def area_of_circle(radius):
    """计算圆的面积"""
    return math.pi * radius ** 2

def area_of_rectangle(length, width):
    """计算矩形的面积"""
    return length * width

calculator/utils/__init__.py

python
# calculator/utils/__init__.py

from .conversion import convert_to_float

__all__ = ['convert_to_float']

calculator/utils/conversion.py

python
# calculator/utils/conversion.py

def convert_to_float(value):
    """将值转换为浮点数"""
    try:
        return float(value)
    except (ValueError, TypeError):
        raise ValueError(f"无法转换为浮点数:{value}")

使用包

python
# 使用 calculator 包
import calculator

print(f"包版本:{calculator.__version__}")
print(f"包作者:{calculator.__author__}")

# 使用算术函数
print(f"add(1, 2) = {calculator.add(1, 2)}")
print(f"subtract(5, 3) = {calculator.subtract(5, 3)}")
print(f"multiply(3, 4) = {calculator.multiply(3, 4)}")
print(f"divide(10, 2) = {calculator.divide(10, 2)}")

# 使用几何函数
print(f"area_of_circle(5) = {calculator.area_of_circle(5)}")
print(f"area_of_rectangle(3, 4) = {calculator.area_of_rectangle(3, 4)}")

# 使用工具函数
print(f"convert_to_float('123') = {calculator.convert_to_float('123')}")
print(f"convert_to_float(456) = {calculator.convert_to_float(456)}")

总结

模块和包是 Python 中组织代码的重要方式,它们可以:

  1. 提高代码的可维护性和可读性
  2. 避免命名冲突
  3. 方便代码的重用
  4. 使代码结构更加清晰

本章节介绍了:

  1. 模块的创建和导入
  2. 包的创建和导入
  3. 相对导入和绝对导入
  4. 模块和包的最佳实践
  5. 模块的加载和缓存

掌握模块和包的使用,对于编写大型 Python 项目非常重要。