【Python基础|DAY05】Python 模块与包

文章目录

  • [Python 模块与包](#Python 模块与包)
    • [1. 什么是功能](#1. 什么是功能)
    • [2. 什么是模块](#2. 什么是模块)
    • [3. 什么是包](#3. 什么是包)
    • [4. 为什么要引入这些概念?](#4. 为什么要引入这些概念?)
    • [5. 如何导入:先理清"路径"与"目标"](#5. 如何导入:先理清“路径”与“目标”)
      • [5.1 情况一:导入整个模块](#5.1 情况一:导入整个模块)
      • [5.2 情况二:导入模块中的具体功能](#5.2 情况二:导入模块中的具体功能)
      • [5.3 * 与 all:精准控制导入范围](#5.3 * 与 all:精准控制导入范围)
        • [5.3.1 从模块导入所有功能](#5.3.1 从模块导入所有功能)
        • [5.3.2 从包导入所有模块](#5.3.2 从包导入所有模块)
      • [5.4 补充技巧:用 as 起别名](#5.4 补充技巧:用 as 起别名)
      • [5.5 示例表](#5.5 示例表)
    • [6. 保护测试代码:if name == "main"](#6. 保护测试代码:if name == "main")

Python 模块与包

当代码量逐渐变大时,把所有逻辑都堆在一个文件里很快就会变得难以维护。Python 提供了功能 → 模块 → 包这一套组合来帮我们组织代码。这篇文章的目标就是理清这三层概念,并彻底搞懂如何正确导入。


1. 什么是功能

定义 :功能就是一段可以完成特定任务的代码块,最典型的就是一个函数

举例:下面的 add 函数实现了一个"加法"功能。

python 复制代码
def add(a, b):
    return a + b

除了函数,类、变量也可以看作模块对外提供的"功能"。


2. 什么是模块

定义 :一个扩展名为 .py 的 Python 文件就是一个模块。模块里可以包含多个函数、类、变量,它们共同完成某一类相关任务。

举例:我们创建一个文件 math_ops.py,它就是一个模块。

python 复制代码
# math_ops.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

3. 什么是包

定义 :当模块越来越多时,我们可以用文件夹来组织它们。一个包含 __init__.py 文件的文件夹,就成为了一个

如果文件夹里没有 __init__.py,那它只是一个普通文件夹,不能被当作包来导入。

__init__.py 有什么用?

  1. 标识包:告诉 Python 这个目录是一个软件包。
  2. 初始化配置 :可以在里面写一些包级别的初始化代码,或者定义 __all__ 列表(后面会详细讲它的用法)。
  3. 简化导入:可以把常用功能提前导入到包的名字空间中,方便外部使用。

举例:假设我们有这样一个目录结构:

复制代码
project/
├── main.py
└── utils/
    ├── __init__.py
    └── string_helpers.py

这里的 utils 就是一个包,因为它包含了 init.py。string_helpers.py 是包内的一个模块。


4. 为什么要引入这些概念?

想象一下,如果把所有的函数、类都塞进一个文件里:

  • 代码一长,找东西和改东西都费劲(难维护);
  • 想在另一个项目里复用某个功能,只能复制粘贴(复用性差);
  • 别人写的函数名可能和你写的冲突(命名冲突)。

通过 模块 来组织代码,可以让项目结构更清晰,复用更方便,还能有效避免命名冲突。


5. 如何导入:先理清"路径"与"目标"

导入时,我们只需要问自己两个问题,就能写出正确的导入语句:

  1. "它在哪里?" 这决定了 from 后面写什么路径
  2. "我要拿什么?" 这决定了 import 后面是写模块名还是功能名,即我们想要获取到的目标

关于路径的起点 :所有的路径,都是相对于当前运行文件所在的目录 作为参考。如果目标模块就在这个目录下,我们就不需要写"包名",也就是 from 可以省略。如果它在某个子文件夹(包)里,我们就需要写出 包名 或者 包名.子包名

为了理清这部分知识,我们假设现在正编写一个文件 main.py,它的同级目录结构如下:

复制代码
project/
├── main.py              # 我们正在写的文件
├── calc.py              # 一个顶层模块,包含 add, subtract 等功能
└── utils/               # 一个包(因为含有 __init__.py)
    ├── __init__.py
    └── helper.py        # 包里的模块,包含 clean_string, format_text 等功能

5.1 情况一:导入整个模块

当我们希望使用一个模块里的多个功能,或者想保留模块名前缀以避免命名冲突时,会选择导入整个模块。

1) 模块就在当前目录下
import 后面直接写文件名(不带 .py)。此时不需要 from,因为 main.pycalc.py 同级,Python 默认会在当前目录寻找。

python 复制代码
import calc

# 使用时必须带上模块名前缀
calc.add(1, 2)
calc.subtract(5, 3)

2) 模块在某个包里
main.pyhelper.py 处于不同目录级别,有两种写法:

  • 写法A:import 包名.模块名
    使用时需要写出完整的路径,前缀较长,但名字冲突风险低。
  • 写法B:from 包名 import 模块名
    这种方式会把模块名直接提到当前空间,使用时只需写模块名。
python 复制代码
# 写法A
import utils.helper
utils.helper.clean_string(" Hello ")

# 写法B
from utils import helper
helper.clean_string(" Hello ")

5.2 情况二:导入模块中的具体功能

当我们只想用模块里的某个函数或类 时,可以直接"指名道姓"地导入。此时 from 绝对不能省略,因为必须告诉 Python 去哪个模块里找这个功能。

1) 功能来自当前目录下的模块

python 复制代码
# 从同目录的 calc 模块中导入 add 函数
from calc import add
print(add(2, 3))   # 直接用功能名,无需写 calc.add

2) 功能来自包里的模块

路径要写完整:包名.模块名

python 复制代码
from utils.helper import clean_string
print(clean_string(" Hello "))

5.3 * 与 all:精准控制导入范围

有时我们想一次性导入某个模块的所有功能,或某个包的所有模块,就会用到 *。但为了避免污染当前命名空间,Python 提供了 __all__ 这个"白名单"机制。

5.3.1 从模块导入所有功能

模块文件 (如 helper.py)中定义 __all__ 列表,from 模块 import *只会 导入列表里列出的名字。

未定义 __all__ 时,会导入所有不以下划线 _ 开头的名字。

python 复制代码
# helper.py
__all__ = ["clean_string", "format_text"]  # 只有这两个会被 * 导入

def clean_string(s):
    ...

def format_text(s):
    ...

def _internal_util():   # 下划线开头,永远不会被 * 导入
    ...
python 复制代码
# main.py
from utils.helper import *
# 现在只能直接使用 clean_string 和 format_text
5.3.2 从包导入所有模块

包的 __init__.py 中定义 __all__ 列表,可以控制 from 包 import * 会导入哪些模块。

python 复制代码
# utils/__init__.py
__all__ = ["helper"]  # 只允许导入 helper 模块
python 复制代码
# main.py
from utils import *
# 现在可以直接使用 helper 模块,utils 包内的其他模块则不会被导入
helper.clean_string(" Hi ")

5.4 补充技巧:用 as 起别名

无论你导入的是模块还是功能,都可以用 as 给它取一个简短易记的别名,这在处理长路径或避免名字冲突时尤其方便。

python 复制代码
import utils.helper as uh
from utils.helper import clean_string as clean

uh.clean_string(" Hi ")
clean(" Hello ")

5.5 示例表

目标 写法示例 from (路径) import (目标) 说明
整个模块(同目录) import calc (省略) 模块名 使用 calc.add()
整个模块(包里) from utils import helper 包名 模块名 使用 helper.clean_string()
单个功能(同目录模块) from calc import add 模块名 功能名 from 不能省
单个功能(包里模块) from utils.helper import clean_string 包.模块名 功能名 路径完整,精确到模块
模块所有功能 from helper import * 模块名 * 模块内 __all__ 控制
包所有模块 from utils import * 包名 * __init__.py __all__ 控制
起别名 import calc as c (视情况) 原名 as 别名 后续用 c.add() 调用

6. 保护测试代码:if name == "main"

我们常常会在模块底部写一些测试代码来验证函数。但如果这个模块被其他文件导入,我们并不希望那些测试代码运行。

解决方法是用一个特殊的变量 __name__

  • 直接运行 该文件时,__name__ 的值被 Python 自动设为 "__main__"
  • 当文件作为模块被导入 时,__name__ 的值则是模块本身的名字(如 "math_ops")。

因此,只要把测试代码放进 if __name__ == "__main__": 里面,就能保证:

直接运行 → 执行测试;被导入 → 测试代码静悄悄,不影响调用方。

python 复制代码
# math_ops.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

if __name__ == "__main__":
    # 这里的代码只在直接运行本文件时执行
    print("测试 add:", add(3, 5))

以上为个人学习总结,旨在梳理个人理解。如有疏漏或不当之处,欢迎指正与交流。

相关推荐
大数据魔法师1 小时前
Streamlit(十一)- API 参考文档(四)- 图表元素
python·web
AllData公司负责人2 小时前
亲测丝滑,体验跃迁|AllData通过集成开源项目Datart,让数据可视化一目了然
java·大数据·数据库·python·数据可视化·数据视图·datart
tang777892 小时前
2026代理IP选型逻辑与成本控制:动态IP VS 静态IP、住宅IP VS 运营商IP VS 数据中心IP的深入解析
爬虫·python·代理ip·住宅ip·住宅代理·运营商ip
AI玫瑰助手2 小时前
Python函数:def定义函数与参数传递基础
android·开发语言·python
24kmaigc2 小时前
NewStarCTF2025-ssti在哪里?-ssrf与ssti注入
python·网络安全·flask·web
老虎海子2 小时前
从零手搓一个 AI 编程助手:Mini Claude Code 完全指南
人工智能·git·vscode·python·github
辞忧九千七3 小时前
吃透Redis7核心数据结构:从基础用法到实战场景(Python版)
开发语言·数据结构·redis·python
空圆小生3 小时前
基于 Python+Vue3 的 AI 人脸识别门禁考勤系统
开发语言·人工智能·python
Yoshizawa-Violet3 小时前
模板方法模式实战:重构Agent工具审批,告别重复代码
python·agent·模板方法