苦练Python第40天:模块加载与项目组织,让代码像乐高一样可复用!

前言

大家好,我是 倔强青铜三 。欢迎关注我,微信公众号:倔强青铜三。点赞、收藏、关注,一键三连!

今天我们聊聊**"怎么把一坨一坨的脚本拆成可维护的模块"**。

当你开始写第二个.py文件时,恭喜你,已经迈出了从"脚本小子"到"工程化开发者"的第一步。

**模块(module)包(package)**是 Python 世界里的第一等公民,搞懂它们,才能让你的代码"写得开、拆得动、跑得稳"。

一口气带你从 "新建一个.py文件" 到 "发布自己的第三方库",全程高能,建议收藏!


🧠 模块到底是什么?

一句话:

模块就是 .py 文件,文件名(去掉 .py)就是模块名。

新建一个 greet.py,内容如下:

python 复制代码
# greet.py
def hello(name: str) -> str:
    return f"Hello, {name}!"

DEFAULT_NAME = "World"

在当前目录打开解释器:

python 复制代码
>>> import greet
>>> greet.hello("Pythonista")
'Hello, Pythonista!'
>>> greet.DEFAULT_NAME
'World'

就这么简单!
import 语句 会把文件从头到尾执行一遍,并把产生的全局命名空间 打包成一个模块对象 ,挂到当前命名空间下。

注意:

  • 模块对象的名字就是文件名(greet)。
  • 模块里定义的变量、函数、类,都变成模块对象的属性。

🔁 模块只加载一次!

Python 为了效率,解释器会话里同名模块只会加载一次

你可以验证:

python 复制代码
>>> import greet
>>> import greet       # 再导入一次
>>> greet.__file__     # 看看文件路径
'/Users/bronze/greet.py'

修改了 greet.py 的内容?

重启解释器,或者使用 importlib.reload

python 复制代码
>>> import importlib, greet
>>> importlib.reload(greet)
<module 'greet'>

📦 四种 import 姿势

姿势 示例 特点
基础导入 import greet greet.xxx 访问,最简洁,不会污染当前命名空间
起别名 import greet as gr 名字太长就给它起外号
精确导入 from greet import hello hello 直接放进当前命名空间,慎用同名覆盖
全部导入 from greet import * 一股脑儿全搬进来,不推荐,会踩命名冲突的坑

🏗️ 把模块当脚本跑:__main__ 魔法

很多时候,我们既希望一个文件"能被 import",又希望"能双击直接跑"。

在文件底部加上:

python 复制代码
if __name__ == "__main__":
    import sys
    name = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_NAME
    print(hello(name))

终端里:

bash 复制代码
$ python greet.py Alice
Hello, Alice!

解释器 import 时,__name__ 等于模块名;当脚本执行时,__name__ 等于 "__main__",所以这段代码只在脚本模式下跑。


🗂️ 项目大了怎么办?------包(package)登场!

想象你写了 N 个模块:

markdown 复制代码
sound/                          顶层包
├── __init__.py                 必须有,哪怕是空文件
├── formats/                    子包
│   ├── __init__.py
│   ├── wavread.py
│   └── wavwrite.py
├── effects/                    子包
│   ├── __init__.py
│   ├── echo.py
│   └── surround.py
└── filters/
    ├── __init__.py
    └── equalizer.py

导入方式一览:

python 复制代码
import sound.effects.echo                 # 绝对导入
from sound.effects import echo            # 子包导入
from sound.effects.echo import echofilter # 精确导入
from . import echo                        # 相对导入(只能在包里用)

🛠️ 模块搜索路径:Python 怎么找到文件?

import 时,解释器按顺序去下面几个地方找:

  1. 内置模块sys.builtin_module_names)。
  2. sys.path 列表,初始化来源:
    • 当前脚本所在目录(或交互模式下的当前目录)。
    • 环境变量 PYTHONPATH 指定的目录。
    • 标准库目录 & site-packages(pip 装的三方库就在这里)。
python 复制代码
>>> import sys, pprint
>>> pprint.pp(sys.path)
['',
 '/usr/local/lib/python3.13',
 '/usr/local/lib/python3.13/site-packages',
 ...]

想把自己的目录塞进去?运行时修改即可:

python 复制代码
import sys
sys.path.insert(0, '/Users/bronze/my_libs')

🧪 实战:写一个带 CLI 的实用模块

需求:写一个数学工具包 mymath,支持求阶乘、斐波那契,还能在命令行里用。

目录结构:

markdown 复制代码
mymath_proj/
├── mymath/
│   ├── __init__.py
│   ├── factorial.py
│   └── fibonacci.py
└── demo.py

1. factorial.py

python 复制代码
def fact(n: int) -> int:
    """递归阶乘"""
    if n < 0:
        raise ValueError("n must be non-negative")
    return 1 if n in (0, 1) else n * fact(n - 1)

2. fibonacci.py

python 复制代码
def fib(n: int) -> list[int]:
    """返回 <= n 的斐波那契数列"""
    a, b = 0, 1
    res = []
    while a <= n:
        res.append(a)
        a, b = b, a + b
    return res

3. init.py 统一导出

python 复制代码
from .factorial import fact
from .fibonacci import fib

__all__ = ["fact", "fib"]
__version__ = "1.0.0"

4. demo.py 脚本入口

python 复制代码
#!/usr/bin/env python3
import argparse, sys, os
# 把项目根目录塞进 sys.path,确保能 import mymath
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))

from mymath import fact, fib

def main():
    parser = argparse.ArgumentParser(description="CLI for mymath")
    sub = parser.add_subparsers(dest="cmd")
    sub.add_parser("fact").add_argument("n", type=int)
    sub.add_parser("fib").add_argument("n", type=int)
    args = parser.parse_args()
    match args.cmd:
        case "fact":
            print(f"{args.n}! =", fact(args.n))
        case "fib":
            print("fib <=", args.n, "->", fib(args.n))
        case _:
            parser.print_help()

if __name__ == "__main__":
    main()

跑一下:

bash 复制代码
$ python demo.py fact 5
5! = 120
$ python demo.py fib 20
fib <= 20 -> [0, 1, 1, 2, 3, 5, 8, 13]

📦 打包发布:让全世界 pip install 你的库!

1. 创建 pyproject.toml(PEP 621 标准)

toml 复制代码
[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mymath-bronze"
version = "1.0.0"
description = "A tiny math toolkit"
authors = [{name="倔强青铜三", email="bronze@example.com"}]
readme = "README.md"
requires-python = ">=3.10"
dependencies = []

[project.scripts]
mymath-cli = "mymath.__main__:main"

2. 新增 main.py,让包自己可执行

python 复制代码
# mymath/__main__.py
from ..demo import main
if __name__ == "__main__":
    main()

3. 构建 & 发布

bash 复制代码
python -m pip install build twine
python -m build                # 生成 dist/*.whl & *.tar.gz
python -m twine upload dist/*  # 需要 PyPI 账号

别人就能:

bash 复制代码
pip install mymath-bronze
python -m mymath fib 100

🧾 速查表:30 秒回顾

场景 代码片段
导入模块 import mod
起别名 import numpy as np
精确导入 from pathlib import Path
相对导入 from .utils import helper
查看已加载模块 sys.modules
查看搜索路径 sys.path
重新加载 importlib.reload(mod)
脚本/模块双用 if __name__ == "__main__"
定义包 目录 + __init__.py
控制 from pkg import * __all__ = ["foo", "bar"]
命令行入口 __main__.pyproject.scripts

🚑 常见坑 & 调试技巧

  1. ImportError / ModuleNotFoundError
    • 检查文件名、目录、虚拟环境、sys.path
  2. 循环导入
    • A 导入 B,B 又导入 A → 拆分公共部分到第三个模块。
  3. 相对导入报错
    • 确保包目录顶层在 sys.path,或 python -m pkg.module 运行。
  4. IDE 提示找不到
    • 把项目根标记为 Sources Root;或配置 PYTHONPATH

✅ 一句话总结

模块让你把代码拆成可复用的小积木;包让你把模块再按层级组织;

掌握 import 的四种姿势、__main__ 的双模式、sys.path 的搜索顺序,

你就能把任何脚本升级成可维护、可分发、可 pip install 的专业级项目!

最后感谢阅读!欢迎关注我,微信公众号:倔强青铜三。欢迎 点赞收藏关注,一键三连!!!

相关推荐
闲人编程3 分钟前
Python第三方库IPFS-API使用详解:构建去中心化应用的完整指南
开发语言·python·去中心化·内存·寻址·存储·ipfs
计算机编程小咖44 分钟前
《基于大数据的农产品交易数据分析与可视化系统》选题不当,毕业答辩可能直接挂科
java·大数据·hadoop·python·数据挖掘·数据分析·spark
居7然1 小时前
大模型微调面试题全解析:从概念到实战
人工智能·微调
zhangfeng11332 小时前
以下是基于图论的归一化切割(Normalized Cut)图像分割工具的完整实现,结合Tkinter界面设计及Python代码示
开发语言·python·图论
haidizym2 小时前
质谱数据分析环节体系整理
大数据·人工智能·数据分析·ai4s
Godspeed Zhao2 小时前
Tesla自动驾驶域控制器产品(AutoPilot HW)的系统化梳理
人工智能·机器学习·自动驾驶
fsnine2 小时前
机器学习案例——预测矿物类型(模型训练)
人工智能·机器学习
数据知道3 小时前
机器翻译60天修炼专栏介绍和目录
人工智能·自然语言处理·机器翻译
flashlight_hi3 小时前
LeetCode 分类刷题:2529. 正整数和负整数的最大计数
python·算法·leetcode
Ashlee_code3 小时前
香港券商櫃台系統跨境金融研究
java·python·科技·金融·架构·系统架构·区块链