模块与包

在Python开发中,随着项目规模增长,将代码拆分到不同文件和目录中变得至关重要。模块正是实现这一目标的基础设施。本文将带你系统掌握模块与包的使用方法,并通过表格对比关键概念,配合大量可运行的代码示例,助你快速上手。


一、模块(Module):一个文件就是一个模块

1.1 什么是模块

任何以.py结尾的Python文件都是一个模块。模块内部可以定义函数、类、变量,也可以包含可执行代码。

# mymodule.py
def greet(name):
    return f"Hello, {name}!"

PI = 3.14159

1.2 创建与使用模块

创建文件mymodule.py

在另一个文件中导入并使用:

# main.py
import mymodule

print(mymodule.greet("Alice"))   # 输出: Hello, Alice!
print(mymodule.PI)               # 输出: 3.14159

1.3 模块的几种导入方式

导入方式 语法示例 调用方式 适用场景
导入整个模块 import math math.sqrt(4) 模块名称短,功能独立
导入特定成员 from math import sqrt, pi sqrt(4) 只用到少数几个成员
导入全部成员 from math import * sqrt(4) 交互式环境或临时测试(不推荐生产)
起别名 import numpy as np np.array([1,2]) 简化长名称或避免命名冲突
# 示例演示
import math
print(math.sqrt(16))          # 4.0

from math import cos, e
print(cos(0))                 # 1.0
print(e)                      # 2.71828

from math import *
print(sin(3.14/2))            # 约 0.999999

import pandas as pd
print(pd.__name__)            # pandas

1.4 __name__ 变量的作用

每个模块都有一个__name__属性。当模块被直接运行时,__name__值为'__main__';当被导入时,值为模块名。常用于编写可测试/可执行的模块代码。

# mymodule.py 增强版
def greet(name):
    return f"Hello, {name}!"

if __name__ == "__main__":
    # 只有在直接运行该脚本时才会执行
    print(greet("World"))

运行python mymodule.py输出:Hello, World!
而在别的文件中导入则不会输出这句话。


二、包(Package):用目录组织多个模块

2.1 什么是包

包是一种通过“__init__.py文件的目录”来组织多个模块的方式。包可以嵌套子包。

2.2 创建包的基本结构

假设我们有一个计算器包:

calculator/
    __init__.py
    basic.py
    advanced.py
  • calculator/__init__.py(可以是空文件,标识该目录是一个包)

  • calculator/basic.py

    def add(a, b):
        return a + b
    def sub(a, b):
        return a - b

  • calculator/advanced.py

    def power(a, b):
        return a ** b
    def sqrt(a):
        return a ** 0.5

2.3 使用包中的模块

# 方法1:导入模块,通过模块访问
import calculator.basic
print(calculator.basic.add(5, 3))   # 8

# 方法2:从包中导入特定模块
from calculator import advanced
print(advanced.power(2, 3))         # 8

# 方法3:直接从模块中导入函数
from calculator.basic import sub
print(sub(10, 4))                   # 6

2.4 __init__.py 的高级用法

__init__.py可以用于:

  • 将包中的核心功能提升到包级别

  • 控制from package import *的行为(定义__all__

  • 执行包的初始化代码

示例:在calculator/__init__.py中写入:

# calculator/__init__.py
from .basic import add, sub
from .advanced import power, sqrt

__all__ = ["add", "sub", "power", "sqrt"]

之后就可以直接:

import calculator
print(calculator.add(2, 3))      # 5
print(calculator.power(2, 3))    # 8

三、模块搜索路径与包导入细节

3.1 搜索顺序

当执行import xxx时,Python按以下顺序搜索:

  1. 当前脚本所在目录

  2. 环境变量PYTHONPATH中的目录

  3. Python标准库目录

  4. 第三方包安装目录(如site-packages

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

python

import sys
for p in sys.path:
    print(p)

3.2 相对导入与绝对导入

在包内部,模块之间互相导入时常用相对导入(以点开头):

text

mypackage/
    __init__.py
    module_a.py
    module_b.py

module_a.py中导入module_b

python

# 绝对导入(推荐)
from mypackage import module_b

# 相对导入(适合包内)
from . import module_b

相对导入不能用于顶层脚本(即__name__ == '__main__'的脚本),只能用于包内的模块。


四、常见问题与最佳实践(表格总结)

常见问题 原因 解决方案
ModuleNotFoundError 模块不在搜索路径中 将模块所在目录添加到sys.path,或安装为包
循环导入 两个模块互相导入 重构代码,将共享依赖移至第三个模块;延迟导入(函数内导入)
相对导入超出顶层包 将包内脚本作为主程序运行 改用绝对导入,或使用python -m package.module方式运行
from xxx import * 导入多余变量 未定义__all__ 在模块/包的__init__.py中明确定义__all__列表

延迟导入示例(解决循环导入)

python

# a.py
def fun_a():
    from b import fun_b   # 在函数内部导入,避免循环
    return fun_b() + 1

五、实战案例:构建一个简易工具包

需求:创建一个字符串处理包 strutils,包含两个模块:case.py(大小写转换)和clean.py(清理空白符)。并提供便捷的包级别接口。

目录结构


strutils/
    __init__.py
    case.py
    clean.py
case.py:
def to_upper(s):
    return s.upper()

def to_lower(s):
    return s.lower()
clean.py:
def strip_whitespace(s):
    return s.strip()

def remove_extra_spaces(s):
    return ' '.join(s.split())
init.py:
from .case import to_upper, to_lower
from .clean import strip_whitespace, remove_extra_spaces

__all__ = ['to_upper', 'to_lower', 'strip_whitespace', 'remove_extra_spaces']

使用示例

import strutils

text = "  Hello   World!  "
print(strutils.to_upper(text))                 # "  HELLO   WORLD!  "
print(strutils.strip_whitespace(text))         # "Hello   World!"
print(strutils.remove_extra_spaces(text))      # "Hello World!"

六、总结

  • 模块是单个.py文件,是包含__init__.py的目录。

  • 合理使用importfrom ... import、别名可以提升代码可读性。

  • __name__ == '__main__' 让模块既可以导入也可以独立运行。

  • 包的__init__.py可以控制包的对外接口和初始化逻辑。

  • 注意相对导入的限制,尽量使用绝对导入以保持清晰。

掌握模块与包,你将能够轻松构建可维护、可扩展的Python项目。快去整理你的代码库吧!

📌 :本文所有代码均已在Python 3.10+环境下测试通过。如有疑问,欢迎评论区交流

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐