Python 库开发完整指南

Python 库开发完整指南 · Python Library Development Guide

Python 库开发零基础完整教程(项目→打包→发布全流程)从项目结构设计到 PyPI 发布,一站式掌握 Python 库开发

什么是 Python 库、项目结构、pyproject.toml 配置、src 布局、编写代码、打包分发、PyPI 发布、版本管理、测试与文档全覆盖

一、什么是 Python 库,解决什么核心问题

Python 库(Library / Package)是一组可复用代码的集合,封装为统一接口供其他项目通过 pip install 安装使用。

❌ 脚本方式(难以复用)

# 复制粘贴 utils.py 到每个项目
# 版本混乱,无统一管理
# 手动处理依赖
# 无法 pip install

代码散落在各项目中,更新需逐一修改,缺乏版本控制和依赖管理。

✅ 标准化库开发

# pip install mylib
from mylib import helper
helper.do_something()
# 版本管理,依赖声明
# 自动处理依赖树

代码集中维护,pip 一键安装,版本语义化,依赖自动解析。

Python 库四大核心价值

代码复用 一次编写,pip install 随处使用,无需复制粘贴

版本管理 语义化版本号,用户锁定特定版本,避免破坏性变更

依赖声明 在 pyproject.toml 中声明依赖,pip 自动解析安装

生态分发 发布到 PyPI 后全球开发者可通过 pip 直接安装

核心定位:将可复用的功能封装为库,通过标准化的打包发布流程,供整个 Python 生态使用。

二、项目结构设计(src layout 标准)

2.1 推荐项目目录结构

现代 Python 库推荐使用 src layout,将库代码放在 src/ 目录下,避免打包时意外导入本地开发环境中的同名包。

mylib/                          # 项目根目录
├── src/                        # 源码目录(src layout)
│   └── mylib/                  # 库包名,与项目名一致或相关
│       ├── __init__.py         # 包入口,暴露对外接口
│       ├── core.py             # 核心功能模块
│       ├── utils.py            # 工具函数
│       └── subpkg/             # 子包(可选)
│           ├── __init__.py
│           └── helpers.py
├── tests/                      # 测试目录
│   ├── __init__.py
│   ├── test_core.py
│   └── test_utils.py
├── docs/                       # 文档目录(可选)
│   ├── guide.md
│   └── api.md
├── pyproject.toml              # 项目元数据与构建配置【核心】
├── README.md                   # 项目说明
├── LICENSE                     # 开源许可证
├── .gitignore                  # Git 忽略规则
└── CHANGELOG.md                # 版本变更日志

❌ 旧版 flat layout

mylib/
├── mylib/          # 代码与项目根混在一起
│   ├── __init__.py
│   └── core.py
├── setup.py        # 旧式配置
└── setup.cfg

打包时可能意外导入本地已安装的版本,导致难以排查的 Bug。

✅ 推荐 src layout

mylib/
├── src/
│   └── mylib/      # 代码在 src/ 下
│       ├── __init__.py
│       └── core.py
├── pyproject.toml   # 现代配置
└── tests/

强制从打包后的安装版本导入,避免本地环境干扰,构建更可靠。

2.2 .gitignore 标准配置

# Python
__pycache__/
*.py[cod]
*.egg-info/
dist/
build/
*.egg

# Virtual Env
.venv/
venv/
.env

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

三、pyproject.toml 完整配置

pyproject.toml 是 Python 项目的现代标准配置文件,替代了旧的 setup.py 和 setup.cfg。PEP 621 定义了其标准格式。

3.1 完整 pyproject.toml 示例

[build-system]
requires = ["setuptools>=75.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"

[project]
name = "mylib"
version = "0.1.0"
description = "A short description of your library"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
keywords = ["example", "template"]

authors = [
  {name = "Your Name", email = "your@email.com"},
]

classifiers = [
  "Development Status :: 3 - Alpha",
  "Intended Audience :: Developers",
  "License :: OSI Approved :: MIT License",
  "Programming Language :: Python :: 3.10",
  "Programming Language :: Python :: 3.11",
  "Programming Language :: Python :: 3.12",
  "Programming Language :: Python :: 3.13",
]

dependencies = [
  "requests>=2.31",
  "pydantic>=2.0",
]

[project.optional-dependencies]
dev = [
  "pytest>=8.0",
  "pytest-cov>=5.0",
  "ruff>=0.5",
  "mypy>=1.10",
  "pre-commit>=3.0",
]
docs = [
  "mkdocs>=1.6",
  "mkdocs-material>=9.0",
]

[project.urls]
Homepage = "https://github.com/yourname/mylib"
Documentation = "https://mylib.readthedocs.io"
Repository = "https://github.com/yourname/mylib"
"Bug Tracker" = "https://github.com/yourname/mylib/issues"

[tool.setuptools.packages.find]
where = ["src"]          # src layout 关键配置
include = ["mylib*"]     # 包含的包名模式

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --cov=mylib"

[tool.ruff]
target-version = "py310"
line-length = 100

[tool.mypy]
python_version = "3.10"
strict = true

3.2 字段说明速查

字段作用是否必填
[build-system]声明构建工具(setuptools / hatchling / flit)
[project] namePyPI 上的包名,不能与已有包冲突
[project] version当前版本号,遵循语义化版本
[project] dependencies运行时依赖列表推荐
[project] requires-python最低 Python 版本约束推荐
[project.optional-dependencies]可选依赖组(dev/docs/test)可选
[tool.setuptools.packages.find]src layout 时必须配置 wheresrc layout 必填

四、编写库代码与 __init__.py

4.1 __init__.py 的职责

__init__.py 是包的入口文件,控制对外暴露的接口。好的库只暴露用户需要使用的 API,隐藏内部实现细节。

# src/mylib/__init__.py
"""mylib - A short description of your library."""

from .core import MyClass, my_function
from .utils import helper

__all__ = [
    "MyClass",
    "my_function",
    "helper",
]

__version__ = "0.1.0"

4.2 核心模块编写示例

# src/mylib/core.py
"""Core module - implements the main functionality."""

from typing import Optional


class MyClass:
    """Main class of the library."""

    def __init__(self, name: str, value: int = 0):
        self.name = name
        self._value = value

    @property
    def value(self) -> int:
        return self._value

    @value.setter
    def value(self, val: int) -> None:
        if val < 0:
            raise ValueError("Value must be non-negative")
        self._value = val

    def greet(self) -> str:
        return f"Hello from {self.name}!"


def my_function(param: str, *, flag: bool = False) -> dict:
    """Main processing function.

    Args:
        param: Input parameter.
        flag: Whether to enable extra processing.

    Returns:
        A dictionary with processed results.
    """
    result = {"input": param, "processed": param.upper()}
    if flag:
        result["extra"] = "enabled"
    return result

4.3 工具模块

# src/mylib/utils.py
"""Utility functions."""

import os
from pathlib import Path
from typing import Union


def ensure_dir(path: Union[str, Path]) -> Path:
    """Ensure a directory exists, creating it if needed.

    Args:
        path: Directory path to ensure.

    Returns:
        The Path object of the directory.
    """
    p = Path(path)
    p.mkdir(parents=True, exist_ok=True)
    return p


def read_file(path: Union[str, Path]) -> str:
    """Read a text file content.

    Args:
        path: Path to the file.

    Returns:
        File content as string.
    """
    with open(path, "r", encoding="utf-8") as f:
        return f.read()
设计原则:每个模块只做一件事,函数和类职责单一。__init__.py 只导入用户需要的公开接口,内部实现模块以下划线开头或放置在子包中。

五、依赖管理与版本控制

5.1 依赖声明原则

依赖声明三条规则

1. 最小版本约束 只声明下界(如 requests>=2.31),不锁死上限,避免与用户其他依赖冲突

2. 按功能分组 运行时依赖放 dependencies,开发工具放 [dev],文档工具放 [docs]

3. 平台特定依赖 使用 environment markers 实现条件依赖

# 条件依赖示例
[project.dependencies]
requests>=2.31
# 仅 Windows 需要
colorama>=0.4 ; sys_platform == "win32"

[project.optional-dependencies]
dev = ["pytest>=8", "ruff>=0.5", "mypy>=1.10"]
docs = ["mkdocs>=1.6", "mkdocs-material>=9"]

5.2 语义化版本(SemVer)

MAJOR . MINOR . PATCH
不兼容 API 变更      向下兼容的新功能      向下兼容的 Bug 修复
1.0.0  →  首次稳定发布
1.2.0  →  新增功能,向后兼容
1.2.1  →  Bug 修复
2.0.0  →  破坏性 API 变更

# 预发布标签
1.0.0a1  →  Alpha 内部测试
1.0.0b1  →  Beta 公开测试
1.0.0rc1 →  Release Candidate 候选版

5.3 CHANGELOG.md 模板

# Changelog

## [0.2.0] - 2026-07-15

### Added
- 新增 `MyClass.greet()` 方法
- 支持 Python 3.13

### Changed
- 重构 utils 模块,废弃 `old_helper()`
- 提升核心算法性能约 30%

### Fixed
- 修复 Windows 下路径分隔符问题

## [0.1.0] - 2026-06-01

### Added
- 初始版本发布
- 核心类 `MyClass` 和工具函数

六、构建与打包(uv / setuptools)

6.1 构建工具对比

工具特点适用场景
setuptoolsPython 生态最成熟,兼容性最好通用项目,推荐新手
hatchling现代简洁,Hatch 生态追求简洁配置的新项目
flit_core极简,纯 Python 包纯 Python 无 C 扩展的项目
pdm-backendPDM 生态,PEP 621 原生使用 PDM 管理的项目
uv极速,Rust 编写,现代工具链追求速度的新项目(推荐)

6.2 使用 uv 构建

# 安装 uv(推荐现代方式)
pip install uv

# 构建 wheel 和 sdist
uv build
Successfully built dist/mylib-0.1.0-py3-none-any.whl
Successfully built dist/mylib-0.1.0.tar.gz

# 查看构建产物
ls dist/
mylib-0.1.0-py3-none-any.whl
mylib-0.1.0.tar.gz

6.3 使用 setuptools 构建

# 安装构建工具
pip install build

# 构建
python -m build
* Creating venv isolated environment...
* Installing packages in isolated environment...
* Building wheel...
Successfully built mylib-0.1.0-py3-none-any.whl
* Building sdist...
Successfully built mylib-0.1.0.tar.gz

6.4 构建产物说明

Wheel (.whl)

预构建的二进制分发格式,安装时无需执行构建步骤,即装即用。

mylib-0.1.0-py3-none-any.whl
├── mylib/
│   ├── __init__.py
│   ├── core.py
│   └── utils.py
└── mylib-0.1.0.dist-info/
    ├── METADATA
    ├── WHEEL
    └── RECORD

Source Distribution (.tar.gz)

源码分发格式,包含源码和 pyproject.toml,安装时在目标机器上构建。

mylib-0.1.0.tar.gz
├── src/
│   └── mylib/
├── pyproject.toml
├── README.md
├── LICENSE
└── PKG-INFO

七、发布到 PyPI 全流程

7.1 发布流程概览

注册 PyPI 创建 Token 构建包 上传发布 pip 安装
  1. 注册 PyPI 账号:pypi.org/account/register 推荐启用双因素认证(2FA)
  2. 创建 API Token:Account Settings → API tokens → Add API token Token 格式:pypi-xxxxxxxx,保存到本地安全位置
  3. 构建 wheel 和 sdist:uv buildpython -m build 产物生成在 dist/ 目录下
  4. 上传到 PyPI: uv publish # 方式一:uv 一键发布 twine upload dist/* # 方式二:传统 twine 上传 首次发布需输入用户名 __token__ 和密码(Token 值)
  5. 验证发布:pip install mylib PyPI 索引更新后即可安装,通常 1-2 分钟

7.2 先发布到 TestPyPI 测试

强烈建议:首次发布前先上传到 TestPyPI 验证,避免正式仓库出现错误。
# 配置 TestPyPI 仓库
uv publish --publish-url https://test.pypi.org/legacy/

# 从 TestPyPI 安装测试
pip install --index-url https://test.pypi.org/simple/ mylib

7.3 发布后验证

# 从 PyPI 安装
pip install mylib

# 检查版本
pip show mylib
Name: mylib
Version: 0.1.0
Summary: A short description of your library
Home-page: https://github.com/yourname/mylib

# 查看依赖
pip show mylib | Select-String "Requires"
Requires: requests, pydantic

八、测试与 CI 集成

8.1 pytest 测试示例

# tests/test_core.py
import pytest
from mylib import MyClass, my_function


class TestMyClass:
    def test_init(self):
        obj = MyClass("test", 42)
        assert obj.name == "test"
        assert obj.value == 42

    def test_greet(self):
        obj = MyClass("world")
        assert obj.greet() == "Hello from world!"

    def test_negative_value(self):
        obj = MyClass("test")
        with pytest.raises(ValueError):
            obj.value = -1


class TestMyFunction:
    def test_basic(self):
        result = my_function("hello")
        assert result["input"] == "hello"
        assert result["processed"] == "HELLO"

    def test_with_flag(self):
        result = my_function("hello", flag=True)
        assert result["extra"] == "enabled"

    @pytest.mark.parametrize("input_val,expected", [
        ("abc", "ABC"),
        ("123", "123"),
        ("", ""),
    ])
    def test_parametrized(self, input_val, expected):
        result = my_function(input_val)
        assert result["processed"] == expected

8.2 运行测试

# 安装测试依赖
pip install -e ".[dev]"

# 运行所有测试
pytest
============================= test session starts =============================
collected 6 items

tests/test_core.py ......                                              [100%]

============================== 6 passed in 0.32s ==============================

# 带覆盖率报告
pytest --cov=mylib --cov-report=term-missing

8.3 GitHub Actions CI 配置

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - run: pip install uv
      - run: uv sync --group dev
      - run: uv run pytest --cov=mylib
      - run: uv run ruff check
      - run: uv run mypy src

九、文档编写与自动化

9.1 README.md 规范

# mylib

[![PyPI](https://img.shields.io/pypi/v/mylib)](https://pypi.org/project/mylib/)
[![Python](https://img.shields.io/pypi/pyversions/mylib)](https://pypi.org/project/mylib/)
[![CI](https://github.com/yourname/mylib/workflows/CI/badge.svg)](https://github.com/yourname/mylib/actions)

A short description of your library.

## Installation

```bash
pip install mylib
```

## Quick Start

```python
from mylib import MyClass

obj = MyClass("world")
print(obj.greet())
# Output: Hello from world!
```

## Documentation

Full documentation: https://mylib.readthedocs.io

## License

This project is licensed under the MIT License.

9.2 MkDocs 文档站点

# mkdocs.yml
site_name: mylib
site_description: A short description of your library
theme:
  name: material
  features:
    - navigation.tabs
    - navigation.sections

nav:
  - Home: index.md
  - Guide:
    - Getting Started: guide/getting-started.md
    - Usage: guide/usage.md
  - API Reference: api/
  - Changelog: changelog.md

plugins:
  - mkdocstrings:
      handlers:
        python:
          paths: [src]
# 安装文档依赖
pip install -e ".[docs]"

# 本地预览文档
mkdocs serve
INFO    -  Building documentation...
INFO    -  Serving on http://127.0.0.1:8000

# 构建静态文档
mkdocs build
INFO    -  Documentation built in site/

十、最佳实践与常见问题

10.1 开发完整工作流

设计接口 编写代码 编写测试 本地构建 TestPyPI PyPI 发布

10.2 最佳实践清单

开发规范

✅ 使用 src layout 组织项目结构,避免导入歧义

✅ 在 __init__.py 中显式定义 __all__,控制暴露接口

✅ 使用有意义的类型注解(Type Hints),提升代码可读性

✅ 所有公有函数编写 docstring,遵循 Google/NumPy 风格

✅ 使用 ruff 规范代码风格,mypy 做静态类型检查

✅ 配置 pre-commit 自动在提交前检查代码

✅ 遵循语义化版本,发布前更新 CHANGELOG

✅ 使用 GitHub Actions 做 CI 自动测试和多版本验证

10.3 常见问题与解决方案

问题现象原因解决方案
pip install 后找不到模块 src layout 未配置 packages.find.where pyproject.toml 中添加 [tool.setuptools.packages.find] where = [“src”]
构建出的包不包含子包 packages.find.include 模式不匹配 设置 include = [“mylib*”] 或已弃用的 namespace_packages
PyPI 发布失败:名称已存在 包名被占用 修改 project.name 为未使用的名称,先搜索 PyPI 确认
twine 上传认证失败 Token 过期或权限不足 重新生成 API Token,确认使用 __token__ 作为用户名
本地测试通过但 CI 失败 本地环境与 CI 不一致 使用 requirements.txt 或 uv.lock 锁定版本,确保 CI 与本地一致
版本号冲突 手动修改版本号后忘记更新 使用 bumpversion 或 hatch version 等工具自动管理版本号
依赖地狱 依赖版本约束过于严格 只设下界不设上界,使用语义化版本范围兼容

10.4 完整项目模板

mylib/
├── .github/workflows/ci.yml
├── src/
│   └── mylib/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   ├── test_core.py
│   └── test_utils.py
├── docs/
│   ├── index.md
│   └── guide/
├── pyproject.toml
├── README.md
├── LICENSE
├── CHANGELOG.md
├── .gitignore
├── .pre-commit-config.yaml
└── mkdocs.yml
全流程总结:设计接口 → src layout 组织代码 → pyproject.toml 配置元数据 → 编写核心功能与类型注解 → pytest 测试覆盖 → 本地构建验证 → TestPyPI 预发布 → 正式发布 PyPI → CI 自动测试 → MkDocs 文档站点。

Python Library Development Complete GuideProject Structure → Build → PyPI Release — Full Workflow

What is a Python library, project structure, pyproject.toml, src layout, coding, packaging, publishing to PyPI, versioning, testing & documentation

1 What is a Python Library

A Python library (package) is a reusable collection of code, packaged with a standardized interface so other projects can install it via pip install.

Four Core Values

Reusability Write once, pip install anywhere — no copy-paste

Versioning Semantic versioning, users pin exact versions, no breaking surprises

Declared Dependencies List dependencies in pyproject.toml, pip auto-resolves

Ecosystem Distribution Publish to PyPI, reachable by every Python developer globally

2 Project Structure & src Layout

Modern Python libraries use src layout — placing source code under src/ to avoid accidental imports from the development environment.

mylib/
├── src/
│   └── mylib/
│       ├── __init__.py
│       ├── core.py
│       ├── utils.py
│       └── subpkg/
│           ├── __init__.py
│           └── helpers.py
├── tests/
│   ├── test_core.py
│   └── test_utils.py
├── docs/
├── pyproject.toml
├── README.md
├── LICENSE
├── .gitignore
└── CHANGELOG.md

3 pyproject.toml Configuration

pyproject.toml is the modern standard configuration file (PEP 621), replacing the legacy setup.py.

[build-system]
requires = ["setuptools>=75.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"

[project]
name = "mylib"
version = "0.1.0"
description = "A short description of your library"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
dependencies = ["requests>=2.31"]

[tool.setuptools.packages.find]
where = ["src"]           # Critical for src layout
include = ["mylib*"]

4 Writing Library Code & __init__.py

The __init__.py controls the public API surface. Expose only what users need.

# src/mylib/__init__.py
from .core import MyClass, my_function

__all__ = ["MyClass", "my_function"]
__version__ = "0.1.0"

# src/mylib/core.py
class MyClass:
    def __init__(self, name: str, value: int = 0):
        self.name = name
        self._value = value

    def greet(self) -> str:
        return f"Hello from {self.name}!"

5 Dependency & Version Management

Semantic Versioning

MAJOR . MINOR . PATCH
Breaking API change      Backward-compatible feature      Backward-compatible bug fix

Dependency Rules

  • Specify only lower bounds: requests>=2.31
  • Group optional deps: dev, docs, test
  • Use environment markers for platform-specific deps

6 Building & Packaging

# Build with uv (recommended)
pip install uv
uv build
Successfully built dist/mylib-0.1.0-py3-none-any.whl
Successfully built dist/mylib-0.1.0.tar.gz

# Or build with setuptools
pip install build
python -m build

7 Publishing to PyPI

Register PyPI Create Token Build Package Upload & Publish pip install
  1. Register at pypi.org (enable 2FA)
  2. Create API Token: Account Settings → API tokens → Add API token
  3. Build: uv build or python -m build
  4. Upload: uv publish or twine upload dist/*
  5. Verify: pip install mylib
Always test on TestPyPI first: uv publish --publish-url https://test.pypi.org/legacy/

8 Testing & CI Integration

# tests/test_core.py
import pytest
from mylib import MyClass

def test_greet():
    obj = MyClass("world")
    assert obj.greet() == "Hello from world!"
pip install -e ".[dev]"
pytest --cov=mylib
============================== 6 passed in 0.32s ==============================

GitHub Actions CI

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13"]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: {python-version: "${{ matrix.python-version }}"}
      - run: pip install uv && uv sync --group dev
      - run: uv run pytest --cov=mylib
      - run: uv run ruff check && uv run mypy src

9 Documentation

README.md with badges

# mylib

[![PyPI](https://img.shields.io/pypi/v/mylib)](https://pypi.org/project/mylib/)
[![Python](https://img.shields.io/pypi/pyversions/mylib)](https://pypi.org/project/mylib/)

A short description of your library.

## Installation
```bash
pip install mylib
```

MkDocs site

pip install -e ".[docs]"
mkdocs serve
Serving on http://127.0.0.1:8000

10 Best Practices & FAQ

Best Practices Checklist

✅ Use src layout to avoid import ambiguity

✅ Define __all__ in __init__.py to control the public API

✅ Add type hints and docstrings to all public functions

✅ Use ruff for linting, mypy for type checking

✅ Follow semantic versioning, update CHANGELOG per release

✅ Set up GitHub Actions CI for multi-version testing

IssueCauseSolution
Module not found after installMissing packages.find in src layoutAdd [tool.setuptools.packages.find] where = [“src”]
PyPI name conflictPackage name takenSearch PyPI first, choose a unique name
Twine auth failureToken expiredRegenerate API token, use __token__ as username
CI fails but local passesEnvironment mismatchLock dependencies with uv.lock or requirements.txt
Dependency hellOverly strict version constraintsOnly set lower bounds, use semver ranges
Full workflow: Design API → src layout → pyproject.toml → Write code with type hints → pytest coverage → Build locally → TestPyPI → PyPI release → CI automation → MkDocs site.