现代 Python 包的标准结构

mypackage/
├── pyproject.toml       项目配置(替代旧的 setup.py)
├── README.md
├── LICENSE
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── core.py
│       └── cli.py
└── tests/
    └── test_core.py

注意 src/ 布局——避免"导入了开发目录而不是已安装的包"这种坑。

pyproject.toml 最小例子

[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
version = "0.1.0"
description = "我的酷酷的工具"
readme = "README.md"
license = { text = "MIT" }
authors = [{ name = "WadeLy", email = "hi@x.com" }]
requires-python = ">=3.10"
dependencies = [
    "requests>=2.31",
    "click>=8.1",
]

[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]

[project.scripts]
mytool = "mypackage.cli:main"     # 装包后产生 mytool 命令

[tool.setuptools.packages.find]
where = ["src"]

本地安装测试

# 开发模式:源码改动立即生效
pip install -e .

# 装可选依赖(dev)
pip install -e ".[dev]"

-e 是 editable——不复制文件,建软链。

构建发布包

pip install build
python -m build

# 产物在 dist/ 目录:
# dist/mypackage-0.1.0-py3-none-any.whl
# dist/mypackage-0.1.0.tar.gz

发到 PyPI

pip install twine
twine upload dist/*

# 测试发布到 TestPyPI
twine upload --repository testpypi dist/*

需要先在 pypi.org 注册账号 + 创建 API token。

poetry:一站式工具(推荐)

pip install poetry
poetry new mypackage         # 建项目骨架
poetry add requests          # 加依赖(自动写到 pyproject.toml)
poetry install               # 装依赖到虚拟环境
poetry run pytest            # 在虚拟环境跑命令
poetry build                 # 打包
poetry publish               # 发布

依赖锁定到 poetry.lock——别人 clone 你的项目能装出完全一样的版本。

uv:2026 年的新选择

pip install uv

uv init mypackage
uv add requests
uv sync                       # 装依赖(比 pip 快 10–100 倍)
uv run pytest

Rust 写的,比 pip / poetry 快几个数量级。新项目优先考虑 uv。

init.py:包的"门面"

# src/mypackage/__init__.py
__version__ = "0.1.0"

from .core import main_function, MainClass

__all__ = ["main_function", "MainClass"]

外部用 from mypackage import main_function 直接引用——不用知道内部模块结构。

版本号:SemVer

0.1.0 三段式:

  • MAJOR.MINOR.PATCH
  • 不兼容改动 → MAJOR + 1
  • 加功能不破坏 → MINOR + 1
  • 修 bug → PATCH + 1
  • 0.x.y = 实验阶段,可以乱改

选谁

场景
学习 / 极简 setuptools + pyproject.toml
现代项目 / 团队协作 poetry
追求极速 / 2026 新项目 uv
老 setup.py 项目 渐进迁移到 pyproject.toml

最小 pyproject.toml + cli 完整例子

[project]
name = "todo"
version = "0.1.0"
dependencies = ["click>=8"]

[project.scripts]
todo = "todo.cli:main"
# src/todo/cli.py
import click

@click.command()
@click.argument("task")
def main(task):
    print(f"加入: {task}")

pip install -e . 之后,终端可以直接 todo "买菜"

下一篇讲 代码风格工具链