🐍 Python Day91:打包与发布 — 让你的代码走向世界
🕐 预计用时:3-4 小时 | 🎯 目标:掌握 setuptools/poetry 打包、PyPI 发布、Docker 容器化
📖 今日目录
1. 为什么要打包?
你写了一个很棒的 Python 工具,想让别人也能用。直接发一个 .py 文件?别人还得装依赖、配环境。打包就是把你的代码变成一个标准格式,别人只需要 pip install your-package 就能用。
2. 项目结构
标准的 Python 项目长这样:
my-awesome-project/
├── src/
│ └── mypackage/
│ ├── __init__.py # 包标识
│ ├── core.py # 核心功能
│ ├── utils.py # 工具函数
│ └── cli.py # 命令行入口
├── tests/
│ ├── __init__.py
│ ├── test_core.py
│ └── test_utils.py
├── pyproject.toml # 项目配置(核心文件)
├── README.md # 项目说明
├── LICENSE # 开源协议
├── .gitignore
└── Dockerfile # Docker 配置
💡 pyproject.toml 是现在 Python 打包的标准配置文件,取代了旧的 setup.py 和 setup.cfg。近年来,几乎所有新项目都用它。
3. setuptools 打包
3.1 最简配置
# pyproject.toml
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-awesome-tool"
version = "0.1.0"
description = "一个很棒的 Python 工具"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.9"
authors = [
{name = "Your Name", email = "you@example.com"},
]
keywords = ["tool", "python"]
classifiers = [
"Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
]
dependencies = [
"requests>=2.28",
"pandas>=1.5",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black",
"flake8",
"mypy",
]
[project.scripts]
my-tool = "mypackage.cli:main"
[project.urls]
Homepage = "https://github.com/yourname/my-awesome-tool"
Documentation = "https://my-awesome-tool.readthedocs.io"
3.2 构建
# 安装构建工具
pip install build
# 构建(生成 dist/ 目录)
python -m build
# 查看产物
ls dist/
# my_awesome_tool-0.1.0-py3-none-any.whl ← wheel 格式(推荐)
# my_awesome_tool-0.1.0.tar.gz ← 源码包
3.3 本地安装测试
# 从 wheel 文件安装
pip install dist/my_awesome_tool-0.1.0-py3-none-any.whl
# 开发模式安装(修改代码后无需重新安装)
pip install -e .
# 验证
python -c "import mypackage; print(mypackage.__version__)"
4. Poetry 现代打包
Poetry 是一个集包管理、依赖管理、打包发布于一体的现代工具。它自动处理依赖冲突,比 pip + setuptools 更省心。
4.1 安装 Poetry
# 安装(官方推荐方式)
curl -sSL https://install.python-poetry.org | python3 -
# 验证
poetry --version
4.2 创建项目
# 新建项目
poetry new my-package
# 目录结构
my-package/
├── my_package/
│ └── __init__.py
├── tests/
│ └── __init__.py
├── pyproject.toml
└── README.md
4.3 pyproject.toml(Poetry 格式)
[tool.poetry]
name = "my-package"
version = "0.1.0"
description = "一个很棒的工具"
authors = ["Your Name"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9"
requests = "^2.28"
pandas = "^1.5"
[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
black = "^24.0"
flake8 = "^7.0"
[tool.poetry.scripts]
my-tool = "my_package.cli:main"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
4.4 常用命令
# 添加依赖
poetry add requests pandas
# 添加开发依赖
poetry add --group dev pytest black
# 安装所有依赖
poetry install
# 运行脚本
poetry run python my_script.py
poetry run pytest
# 构建
poetry build
# 发布
poetry publish
| | |
|---|
| python -m venv venv | |
| pip install -r requirements.txt | poetry install |
| | poetry add xxx |
| pip freeze | |
| python -m build | poetry build |
| twine upload dist/* | poetry publish |
5. 发布到 PyPI
PyPI(Python Package Index)是 Python 的官方包仓库。pip install requests 就是从 PyPI 下载的。
5.1 注册账号
- 生成 API Token(推荐):Account Settings → API tokens → Add API token
5.2 用 twine 上传
# 安装 twine
pip install twine
# 构建
python -m build
# 上传到 TestPyPI(测试用,先别传正式的)
twine upload --repository testpypi dist/*
# 上传到 PyPI(正式发布)
twine upload dist/*
# 输入用户名和 API Token
5.3 用 Poetry 上传
# 配置 PyPI token
poetry config pypi-token.pypi your-api-token
# 构建并发布
poetry publish --build
5.4 从 TestPyPI 安装测试
# 从 TestPyPI 安装
pip install --index-url https://test.pypi.org/simple/ my-awesome-tool
# 验证
my-tool --version
⚠️ 发布前检查清单:
• 包名是否已被占用?(去 PyPI 搜索)
• README.md 是否完善?(PyPI 页面会显示)
• 版本号是否正确?
• LICENSE 文件是否存在?
• 依赖列表是否完整?
6. Docker 容器化
6.1 Docker 是什么?
想象你搬家——Docker 就是一个集装箱,把你的代码、Python 版本、所有依赖、系统库全部打包在一起。不管搬到哪台服务器,打开就能跑。
6.2 编写 Dockerfile
# Dockerfile — 构建镜像的"配方"
# 基础镜像(选择轻量版 Python)
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 复制依赖文件(利用 Docker 缓存层)
COPY pyproject.toml poetry.lock* ./
# 安装依赖
RUN pip install poetry && \
poetry config virtualenvs.create false && \
poetry install --without dev --no-interaction
# 复制项目代码
COPY . .
# 暴露端口(如果是 Web 应用)
EXPOSE 8000
# 启动命令
CMD ["python", "-m", "mypackage.cli"]
6.3 构建和运行
# 构建镜像
docker build -t my-awesome-tool:0.1.0 .
# 查看镜像
docker images | grep my-awesome-tool
# 运行容器
docker run --rm my-awesome-tool:0.1.0
# 运行 Web 应用(映射端口)
docker run -p 8000:8000 my-awesome-tool:0.1.0
# 进入容器调试
docker run -it my-awesome-tool:0.1.0 /bin/bash
6.4 多阶段构建(优化体积)
# Dockerfile.multi — 多阶段构建
# 阶段1:构建
FROM python:3.11-slim AS builder
WORKDIR /app
COPY pyproject.toml poetry.lock* ./
RUN pip install poetry && poetry install --without dev --no-interaction
# 阶段2:运行(只保留运行时需要的东西)
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY src/ ./src/
CMD ["python", "-m", "mypackage.cli"]
6.5 docker-compose(多服务编排)
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
depends_on:
- db
- redis
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
pgdata:
# 启动所有服务
docker-compose up -d
# 查看日志
docker-compose logs -f web
# 停止
docker-compose down
🐳 Docker 最佳实践:
• 用 slim 或 alpine 基础镜像(体积小)
• 先复制依赖文件,再复制代码(利用缓存)
• 不要在镜像里存密码(用环境变量或 secrets)
• 用 .dockerignore 排除不需要的文件
7. 今日练习
练习 1:打包你的计算器
把 Day90 的计算器模块打包成一个 pip 包,本地 pip install -e . 测试。
练习 2:Poetry 项目
用 poetry new 创建一个新项目,添加 requests 依赖,写一个天气查询 CLI 工具。
练习 3:Docker 化
给你的 Flask 博客项目(Day58)写一个 Dockerfile,用 docker-compose 编排 Web + 数据库。
8. 今日小结
🎯 一句话总结:打包让别人能装你的代码,PyPI让别人能找到你的代码,Docker让别人能跑你的代码。三步走完,你的 Python 项目就"上线"了。
🔮 明天预告:Day92 我们学习 Git 版本控制——代码的"时光机"。init、add、commit、push、pull,分支策略和协作流程,这是团队开发的基石!