Python

技术栈
后端框架
python编程语言解释型动态类型胶水语言脚本语言

概览

Python 技术栈概览

Python 是由 Guido van Rossum 于 1991 年首次发布的高级、解释型、通用编程语言。它以简洁优雅的语法和"可读性优先"的设计哲学著称,是全球最流行的编程语言之一。

是什么

Python 是一门动态类型、垃圾回收、多范式的编程语言,支持面向对象、函数式、过程式编程。它拥有"电池全含"(Batteries Included)的标准库,覆盖文件I/O、网络、数据库、测试、GUI 等场景。

解决什么问题

  • 快速原型开发:简洁语法让开发者能用极短代码表达复杂逻辑
  • 自动化脚本:系统管理、数据处理、测试自动化等场景的首选
  • 数据科学与 AI:NumPy、Pandas、PyTorch、TensorFlow 构建了最活跃的科学生态
  • Web 后端:Django、Flask、FastAPI 覆盖从简单到企业级的 Web 开发
  • 胶水语言:轻松调用 C/C++/Java 库,打通异构系统
  • 教育与编程入门:接近自然语言的语法降低学习门槛

关键特性

  • 简洁可读:缩进定义代码块,强制统一代码风格
  • 动态类型 + 强类型:运行时确定类型,但类型不会隐式转换
  • 丰富的标准库:正则、JSON、SQLite、HTTP、日志等开箱即用
  • 庞大的第三方生态:PyPI 拥有 50万+ 包
  • 跨平台:Windows / Linux / macOS 一致运行
  • CPython + 多实现:除官方 CPython 外还有 PyPy(JIT)、Jython(JVM)、IronPython(.NET)

安装

Python 安装指南

1. 环境准备

操作系统支持

  • Windows:Windows 10/11(x86-64 / ARM64)
  • macOS:macOS 11 Big Sur 及以上(Intel / Apple Silicon)
  • Linux:主流发行版(Ubuntu 20.04+/Debian 11+/CentOS 8+/Fedora 36+ 等)

运行时版本

  • 推荐使用 Python 3.11+(性能大幅提升)
  • 生产环境建议 3.103.12(长期支持更稳定)
  • ⚠️ Python 2.7 已于 2020 年停止维护,请勿用于新项目

依赖项

  • pip:Python 包管理器(Python 3.4+ 自带)
  • venv:虚拟环境模块(Python 3.3+ 自带)
  • 可选:pyenv(多版本管理)、poetry(现代依赖管理)

2. 安装步骤

Windows

# 方法一:winget(推荐)
winget install Python.Python.3.12

# 方法二:Microsoft Store
# 搜索 "Python 3.12" → 点击安装

# 方法三:官网安装包
# 访问 https://www.python.org/downloads/
# 下载 .exe 安装包,安装时务必勾选 ☑ "Add Python to PATH"

# 验证安装
python --version
pip --version

macOS

# 方法一:Homebrew(推荐)
brew install python@3.12

# 方法二:pyenv(多版本管理)
brew install pyenv
pyenv install 3.12.0
pyenv global 3.12.0

# 方法三:官网安装包
# https://www.python.org/downloads/macos/

# 验证安装
python3 --version
pip3 --version

Linux (Ubuntu/Debian)

# 方法一:deadSnakes PPA(推荐,获取最新版本)
sudo apt update
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:deadsnakes/ppa -y
sudo apt install python3.12 python3.12-venv python3.12-dev -y

# 方法二:源码编译(完全自定义)
sudo apt install build-essential zlib1g-dev libncurses5-dev \
  libgdbm-dev libnss3-dev libssl-dev libreadline-dev \
  libffi-dev libsqlite3-dev wget libbz2-dev -y

wget https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz
tar -xf Python-3.12.0.tgz
cd Python-3.12.0
./configure --enable-optimizations --prefix=/usr/local
make -j$(nproc)
sudo make altinstall

# 验证安装
python3.12 --version
pip3.12 --version

Linux (CentOS/Fedora/RHEL)

# Fedora
sudo dnf install python3.12

# CentOS/RHEL 8+
sudo dnf install python39  # 或启用 EPEL 获取更新版本

# 验证
python3 --version

3. 安装后配置

# 创建虚拟环境(防止全局污染)
python3 -m venv myproject
source myproject/bin/activate  # Linux/macOS
# myproject\Scripts\activate   # Windows

# 升级 pip 到最新版
pip install --upgrade pip

# 安装常用工具
pip install ipython      # 增强交互式解释器
pip install black        # 代码格式化
pip install ruff         # 快速 Linter
pip install mypy         # 静态类型检查

4. 常见问题

Q: pip install 报 "Permission denied"

# 不要用 sudo pip install!使用用户安装或虚拟环境
pip install --user <;package>        # 用户级安装
python3 -m venv venv &;& source venv/bin/activate  # 虚拟环境(推荐)

Q: pip 下载速度太慢

# 使用国内镜像
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple <package>

# 永久配置(Linux/macOS:~/.pip/pip.conf,Windows:%APPDATA%\pip\pip.ini)
# [global]
# index-url = https://pypi.tuna.tsinghua.edu.cn/simple

Q: macOS 上 python 和 python3 的区别

  • macOS 自带 /usr/bin/python3(通常是旧版本),Homebrew 安装的在 /usr/local/bin/python3
  • 建议用 python3 命令,或通过 brew link / alias 统一

Q: 多个 Python 版本共存如何管理?

# pyenv 是最佳方案
pyenv versions          # 列出已安装版本
pyenv global 3.12.0     # 设置全局默认
pyenv local 3.11.5      # 为当前目录设置局部版本

Q: SSL 证书错误

# macOS
/Applications/Python\ 3.12/Install\ Certificates.command

# Linux(源码编译时)
sudo apt install libssl-dev  # 重新编译前确保安装

示例

Python 进阶示例:装饰器、生成器与上下文管理器

目标

掌握 Python 三大进阶特性:装饰器生成器上下文管理器,理解它们在真实项目中的应用。


1. 装饰器(Decorator)

装饰器在不修改原函数的情况下,为其增加额外功能。

基础装饰器

import time
import functools
from typing import Callable, Any


def timer(func: Callable) -> Callable:
    """测量函数执行时间的装饰器"""

    @functools.wraps(func)  # 保留原函数的元信息
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"[timer] {func.__name__} 执行耗时: {elapsed:.4f} 秒")
        return result

    return wrapper


@timer
def compute_factorial(n: int) -> int:
    """计算阶乘(故意用慢方法演示)"""
    if n <= 1:
        return 1
    time.sleep(0.1)  # 模拟耗时操作
    return n * compute_factorial(n - 1)


print(f"5! = {compute_factorial(5)}")

带参数的装饰器

def retry(max_attempts: int = 3, delay: float = 1.0):
    """失败自动重试的装饰器(装饰器工厂)"""

    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts:
                        raise
                    print(f"[retry] {func.__name__} 第 {attempt} 次失败: {e},{delay}秒后重试...")
                    time.sleep(delay)
            return None

        return wrapper

    return decorator


@retry(max_attempts=3, delay=0.5)
def unstable_network_call() -> str:
    """模拟不稳定的网络请求"""
    import random
    if random.random() < 0.7:
        raise ConnectionError("网络超时")
    return "数据获取成功!"


# 多次运行观察重试行为
for _ in range(3):
    try:
        print(unstable_network_call())
    except ConnectionError:
        print("最终失败")
    print("---")

类装饰器:注册模式

# 实际用途:插件系统、命令注册
handlers: dict[str, Callable] = {}


def register_command(name: str):
    """将函数注册为命令处理器"""

    def decorator(func: Callable) -> Callable:
        handlers[name] = func
        return func

    return decorator


@register_command("greet")
def handle_greet(user: str) -> str:
    return f"你好,{user}!"


@register_command("bye")
def handle_bye(user: str) -> str:
    return f"再见,{user}!"


# 动态分发
command = "greet"
if command in handlers:
    print(handlers[command]("张三"))

2. 生成器(Generator)

生成器用 yield 惰性产生值,节省内存。

def fibonacci(n: int):
    """生成前 n 个斐波那契数"""
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b


# 惰性求值:不会一次性计算所有值
fib = fibonacci(50)  # 没问题,还没计算
print(list(fib))     # 现在才计算前50个


def read_large_file(filepath: str):
    """逐行读取大文件(不会撑爆内存)"""
    with open(filepath, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()


# 生成器表达式(类似列表推导式但惰性)
squares = (x**2 for x in range(10**9))  # 立即返回,几乎不占内存
print(next(squares))  # 0
print(next(squares))  # 1
print(next(squares))  # 4


def pipeline(data):
    """生成器管道:链式处理数据流"""
    # 步骤1:过滤偶数
    evens = (x for x in data if x % 2 == 0)
    # 步骤2:平方
    squared = (x**2 for x in evens)
    # 步骤3:转字符串
    return (f"结果: {x}" for x in squared)


data = range(20)
for item in pipeline(data):
    print(item)

yield from:委托子生成器

def flatten(nested):
    """递归展平嵌套列表"""
    for item in nested:
        if isinstance(item, (list, tuple)):
            yield from flatten(item)  # 委托给子生成器
        else:
            yield item


nested_list = [1, [2, [3, 4], 5], 6, [7, 8]]
print(list(flatten(nested_list)))  # [1, 2, 3, 4, 5, 6, 7, 8]

3. 上下文管理器(Context Manager)

with 语句确保资源正确获取和释放。

基于类

class DatabaseConnection:
    """模拟数据库连接(上下文管理器)"""

    def __init__(self, db_url: str):
        self.db_url = db_url
        self.connected = False

    def __enter__(self):
        print(f"  连接数据库: {self.db_url}")
        self.connected = True
        return self  # 返回给 as 子句

    def query(self, sql: str) -> str:
        if not self.connected:
            raise RuntimeError("未连接数据库")
        return f"执行: {sql} → 结果集"

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"  断开数据库连接")
        self.connected = False
        # 返回 True 会吞掉异常(一般返回 False)
        return False


# 使用
with DatabaseConnection("postgresql://localhost/mydb") as db:
    print(db.query("SELECT * FROM users"))
# 离开 with 块时自动断开连接

基于生成器(contextlib)

from contextlib import contextmanager


@contextmanager
def temp_dir(path: str = "/tmp/python_demo"):
    """临时目录上下文管理器"""
    import shutil
    import os

    # __enter__ 部分
    os.makedirs(path, exist_ok=True)
    print(f"创建临时目录: {path}")

    try:
        yield path  # 交给 with 块
    finally:
        # __exit__ 部分(无论如何都会执行)
        shutil.rmtree(path, ignore_errors=True)
        print(f"清理临时目录: {path}")


with temp_dir() as tmp:
    # 在临时目录中做操作
    print(f"  在 {tmp} 中工作...")

实用组合:计时 + 临时环境

@contextmanager
def timer_context(label: str = "操作"):
    """测量代码块耗时的上下文管理器"""
    start = time.perf_counter()
    yield
    elapsed = time.perf_counter() - start
    print(f"[{label}] 耗时: {elapsed:.4f}s")


with timer_context("数据处理"):
    # 任何需要计时的代码块
    result = sum(range(10_000_000))
    print(f"  计算结果: {result}")

运行步骤

python3 advanced_features.py

预期输出(节选)

5! = 120
[timer] compute_factorial 执行耗时: 0.5032 秒

[retry] unstable_network_call 第 1 次失败: 网络超时,0.5秒后重试...
数据获取成功!

你好,张三!

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...]

结果: 0
结果: 4
结果: 16
...

[1, 2, 3, 4, 5, 6, 7, 8]

  连接数据库: postgresql://localhost/mydb
执行: SELECT * FROM users → 结果集
  断开数据库连接

创建临时目录: /tmp/python_demo
  在 /tmp/python_demo 中工作...
清理临时目录: /tmp/python_demo

关键要点

特性 使用场景
装饰器 日志、计时、权限校验、缓存、重试
生成器 大文件处理、无限序列、数据管道
上下文管理器 文件/连接/锁管理、临时状态、计时

Python Hello World 与基本语法

目标

运行第一个 Python 程序,掌握基本语法结构:变量、数据类型、条件、循环、函数。

完整代码

#!/usr/bin/env python3
"""Python 入门示例:Hello World 与核心语法"""

# ========== 1. Hello World ==========
print("Hello, Vibe!")

# ========== 2. 变量与数据类型 ==========
name: str = "Python"            # 字符串(类型注解可选)
version: float = 3.12           # 浮点数
release_year: int = 2024        # 整数
is_awesome: bool = True         # 布尔值
features: list = ["简洁", "优雅", "强大"]  # 列表
metadata: dict = {"作者": "Guido van Rossum", "范式": "多范式"}  # 字典

print(f"{name} {version} 发布于 {release_year},特点:{', '.join(features)}")

# ========== 3. 条件判断 ==========
score = 85
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "D"
print(f"分数 {score} → 等级 {grade}")

# ========== 4. 循环 ==========
# for 循环
print("\n--- for 循环 ---")
for i, feature in enumerate(features, start=1):
    print(f"  {i}. {feature}")

# while 循环
print("\n--- while 循环 ---")
count = 3
while count > 0:
    print(f"  倒计时: {count}")
    count -= 1
print("  发射!🚀")

# 列表推导式(Python 特色)
squares = [x**2 for x in range(1, 6)]
print(f"\n平方数: {squares}")

# ========== 5. 函数 ==========
def greet(user: str, language: str = "zh") -> str:
    """按语言打招呼"""
    messages = {
        "zh": f"你好,{user}!",
        "en": f"Hello, {user}!",
        "fr": f"Bonjour, {user}!",
    }
    return messages.get(language, messages["en"])

print(f"\n{greet('开发者')}")
print(greet("Developer", "en"))

# ========== 6. 异常处理 ==========
def safe_divide(a: float, b: float) -> float | None:
    """安全除法"""
    try:
        return a / b
    except ZeroDivisionError:
        print(f"错误: 不能除以 0")
        return None
    except TypeError:
        print(f"错误: 参数类型不正确")
        return None

print(f"\n10 / 3 = {safe_divide(10, 3)}")
print(f"10 / 0 = {safe_divide(10, 0)}")

# ========== 7. 类与对象 ==========
class Dog:
    """一个简单的狗类"""

    def __init__(self, name: str, breed: str):
        self.name = name
        self.breed = breed

    def bark(self) -> str:
        return f"{self.name} (品种: {self.breed}) 在叫: 汪汪!"

dog = Dog("旺财", "金毛")
print(f"\n{dog.bark()}")

# ========== 8. 文件读写 ==========
from pathlib import Path

output = Path("/tmp/hello_python.txt")
output.write_text("Python 让编程变得简单而有趣!", encoding="utf-8")
content = output.read_text(encoding="utf-8")
print(f"\n文件内容: {content}")

运行步骤

# 保存为 hello.py,然后运行
python3 hello.py

预期输出

Hello, Vibe!
Python 3.12 发布于 2024,特点:简洁, 优雅, 强大
分数 85 → 等级 B

--- for 循环 ---
  1. 简洁
  2. 优雅
  3. 强大

--- while 循环 ---
  倒计时: 3
  倒计时: 2
  倒计时: 1
  发射!🚀

平方数: [1, 4, 9, 16, 25]

你好,开发者!
Hello, Developer!

10 / 3 = 3.3333333333333335
错误: 不能除以 0
10 / 0 = None

旺财 (品种: 金毛) 在叫: 汪汪!

文件内容: Python 让编程变得简单而有趣!

关键要点

特性 说明
print() 内置打印函数,自动换行
f-string f"{变量}" 格式化字符串,Python 3.6+
类型注解 name: str 提高可读性,运行时不影响
列表推导式 [expr for item in iterable] 简洁优雅
with 语句 自动管理资源(文件、连接等)
pathlib 现代文件路径处理(Python 3.4+)

教程

Python 从入门到进阶 - 第一篇:基础语法与编程思维

前言

这篇教程面向有基本计算机操作经验但编程零基础的学习者。我们的目标不是罗列语法,而是建立编程思维——学会用 Python 表达逻辑、解决问题。


第 1 章:认识 Python

1.1 Python 是什么?

想象你有一个非常聪明的助手,你用简单的英语告诉他要做什么,他就能执行。Python 就像是这样一个助手——它是一种接近人类自然语言的编程语言。

# 这就是完整的 Python 程序!
print("你好,世界!")

1.2 为什么选择 Python?

对比维度 Python C++ Java
Hello World 代码行数 1 行 5+ 行 5+ 行
是否需要编译 否(解释执行)
内存管理 自动(垃圾回收) 手动 自动
学习曲线 平缓 陡峭 中等

Python 的核心哲学浓缩在 import this 中:

优美胜于丑陋,明了胜于隐晦。
简洁胜于复杂,复杂胜于凌乱。
可读性很重要。

可以在 Python 解释器中输入 import this 查看完整版。


第 2 章:变量与数据类型

2.1 变量 = 贴标签

把变量理解为给数据「贴标签」,而不是「盒子」:

x = 42          # 整数
y = 3.14        # 浮点数
name = "Alice"  # 字符串
is_valid = True # 布尔值

# Python 的动态类型:变量可以重新绑定到不同类型
x = "现在是字符串了"
print(x)  # 现在是字符串了

2.2 核心数据类型

# 数字
a = 10 + 5        # 加法
a = 10 ** 3       # 幂运算 → 1000
a = 17 // 3       # 整除 → 5
a = 17 % 3        # 取余 → 2

# 字符串(不可变)
s = "Hello"
s = s + " World"  # 拼接 → "Hello World"
s = f"1+1={1+1}"  # f-string → "1+1=2"
s = s.upper()      # 转大写
s = s[0:5]         # 切片 → 前5个字符

# 列表(可变,有序)
nums = [1, 2, 3]
nums.append(4)      # [1, 2, 3, 4]
nums.insert(0, 0)   # [0, 1, 2, 3, 4]
last = nums.pop()   # last=4, nums=[0,1,2,3]

# 元组(不可变,有序)
point = (3, 4)
x, y = point       # 解包赋值

# 字典(键值对,Python 3.7+ 保序)
user = {"name": "张三", "age": 25}
user["email"] = "zhangsan@example.com"
age = user.get("age", 0)  # 安全获取,不存在时返回默认值

# 集合(无序,不重复)
tags = {"python", "编程", "入门"}
tags.add("python")  # 重复添加无效!

2.3 类型转换

int("42")      # 字符串 → 整数: 42
str(3.14)      # 数字 → 字符串: "3.14"
float("3.14")  # 字符串 → 浮点数: 3.14
bool("")       # 空字符串 → False
bool("hello")  # 非空字符串 → True
list("abc")    # 字符串 → 列表: ["a", "b", "c"]

⚠️ 思考:int("3.14") 会报错,为什么?因为 "3.14" 不是合法整数字符串。应该先 floatint


第 3 章:控制流

3.1 条件判断

age = 18

if age >= 18:
    print("成年人")
elif age >= 13:
    print("青少年")
else:
    print("儿童")

# 三元表达式
status = "成年" if age >= 18 else "未成年"

# 注意:Python 用缩进定义代码块!推荐 4 个空格

3.2 循环

# ===== for 循环:遍历可迭代对象 =====
fruits = ["苹果", "香蕉", "橘子"]

# 方式一:直接遍历元素
for fruit in fruits:
    print(f"我喜欢{fruit}")

# 方式二:需要索引时用 enumerate
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")

# 方式三:同时遍历两个列表用 zip
names = ["Alice", "Bob"]
scores = [85, 92]
for name, score in zip(names, scores):
    print(f"{name}: {score}分")

# range():生成数字序列
for i in range(5):       # 0, 1, 2, 3, 4
    print(i)
for i in range(2, 6):    # 2, 3, 4, 5
    print(i)
for i in range(0, 10, 2): # 0, 2, 4, 6, 8(步长2)
    print(i)

# ===== while 循环:满足条件时重复 =====
count = 0
while count < 3:
    print(f"第 {count+1} 次")
    count += 1

# break:跳出循环
# continue:跳过本次迭代
for i in range(10):
    if i == 5:
        break       # 到 5 就停止
    if i % 2 == 0:
        continue    # 跳过偶数
    print(i)        # 只打印 1, 3

第 4 章:函数

4.1 定义与调用

def 函数名(参数1, 参数2=默认值):
    """文档字符串:说明函数做什么"""
    # 函数体
    return 返回值

实战示例:

def calculate_bmi(weight_kg: float, height_m: float) -> str:
    """
    计算 BMI 并返回评估结果。

    参数:
        weight_kg: 体重(千克)
        height_m: 身高(米)

    返回:
        BMI 评估字符串
    """
    if height_m <= 0:
        raise ValueError("身高必须大于 0")

    bmi = weight_kg / (height_m ** 2)

    if bmi < 18.5:
        return f"BMI={bmi:.1f},偏瘦"
    elif bmi < 24:
        return f"BMI={bmi:.1f},正常"
    elif bmi < 28:
        return f"BMI={bmi:.1f},偏胖"
    else:
        return f"BMI={bmi:.1f},肥胖"

# 调用
print(calculate_bmi(70, 1.75))  # BMI=22.9,正常
print(calculate_bmi(85, 1.70))  # BMI=29.4,肥胖

4.2 参数传递

# 位置参数
def add(a, b):
    return a + b
add(1, 2)  # a=1, b=2

# 关键字参数
add(b=2, a=1)  # 顺序无所谓

# 默认参数
def greet(name, greeting="你好"):
    return f"{greeting},{name}!"
greet("小明")             # "你好,小明!"
greet("XiaoMing", "Hello") # "Hello,XiaoMing!"

# 可变参数
def sum_all(*args):
    return sum(args)
sum_all(1, 2, 3, 4)  # 10

# 关键字可变参数
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")
print_info(name="张三", age=25, city="北京")

第 5 章:类与面向对象

5.1 为什么需要类?

当数据和行为紧密相关时,用类来组织代码:

class BankAccount:
    """银行账户类"""

    # 类属性(所有实例共享)
    bank_name = "Python 银行"

    def __init__(self, owner: str, balance: float = 0):
        """构造函数:初始化实例"""
        self.owner = owner          # 实例属性
        self.balance = balance
        self._transactions = []     # 私有约定(单下划线)

    def deposit(self, amount: float) -> bool:
        """存款"""
        if amount <= 0:
            print("存款金额必须大于 0")
            return False
        self.balance += amount
        self._transactions.append(f"+{amount}")
        print(f"存入 {amount} 元,余额: {self.balance} 元")
        return True

    def withdraw(self, amount: float) -> bool:
        """取款"""
        if amount <= 0:
            print("取款金额必须大于 0")
            return False
        if amount > self.balance:
            print(f"余额不足!当前余额: {self.balance} 元")
            return False
        self.balance -= amount
        self._transactions.append(f"-{amount}")
        print(f"取出 {amount} 元,余额: {self.balance} 元")
        return True

    @property
    def transaction_count(self) -> int:
        """计算属性:交易次数"""
        return len(self._transactions)

    def __str__(self) -> str:
        """字符串表示"""
        return f"BankAccount(owner={self.owner}, balance={self.balance})"


# 使用
account = BankAccount("张三", 1000)
account.deposit(500)        # 存入 500 元,余额: 1500 元
account.withdraw(200)       # 取出 200 元,余额: 1300 元
account.withdraw(5000)      # 余额不足!
print(account)              # BankAccount(owner=张三, balance=1300)
print(f"共 {account.transaction_count} 笔交易")

5.2 继承

class SavingsAccount(BankAccount):
    """储蓄账户,继承 BankAccount"""

    def __init__(self, owner: str, balance: float = 0, interest_rate: float = 0.02):
        super().__init__(owner, balance)  # 调用父类构造
        self.interest_rate = interest_rate

    def add_interest(self):
        """计算利息"""
        interest = self.balance * self.interest_rate
        self.deposit(interest)
        print(f"利息 {interest:.2f} 元已到账")

    # 重写父类方法
    def withdraw(self, amount: float) -> bool:
        """储蓄账户取款收手续费 1 元"""
        return super().withdraw(amount + 1)

第 6 章:模块与包

# 导入整个模块
import math
print(math.sqrt(16))  # 4.0

# 导入特定函数
from datetime import datetime, timedelta
now = datetime.now()

# 导入所有(不推荐,会污染命名空间)
# from math import *

# 别名
import numpy as np
import pandas as pd

# 自定义模块
# my_utils.py:
#   def double(x): return x * 2
# 然后在另一个文件中:
# from my_utils import double

思考题

  1. a = [1, 2, 3] 然后 b = a,修改 b[0] = 99a 会变吗?为什么?
  2. 什么时候用元组,什么时候用列表?
  3. 函数参数 def f(a, lst=[]) 有什么陷阱?
  4. is== 有什么区别?

答案提示:1. 会变,因为列表是可变对象,b = a 只是引用传递;2. 需要修改用列表,不变数据用元组;3. 默认参数只在函数定义时计算一次,可变默认参数会导致累积;4. is 检查身份(同一对象),== 检查值相等。


Python 实战篇:构建命令行待办事项应用

前言

学完基础语法后,最好的巩固方式就是动手做一个完整的项目。这篇教程带你从零构建一个命令行待办事项(Todo)应用,涵盖文件持久化、命令解析、错误处理等真实场景。


第 1 章:需求分析

功能列表

  • 添加任务:python todo.py add "买牛奶"
  • 列出所有任务:python todo.py list
  • 完成任务:python todo.py done 1(按编号)
  • 删除任务:python todo.py delete 1
  • 数据持久化到 JSON 文件

项目结构

todo/
├── todo.py          # 入口 + 命令行解析
├── models.py        # 数据模型
├── storage.py       # JSON 文件读写
└── tasks.json       # 持久化文件(运行时生成)

第 2 章:数据模型

创建 models.py

"""数据模型模块"""
from dataclasses import dataclass, field, asdict
from datetime import datetime
from typing import Optional


@dataclass
class Task:
    """任务数据类"""

    title: str
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    done: bool = False
    done_at: Optional[str] = None

    def mark_done(self) -> None:
        """标记完成"""
        self.done = True
        self.done_at = datetime.now().isoformat()

    def to_dict(self) -> dict:
        """转为字典(用于 JSON 序列化)"""
        return asdict(self)

    @classmethod
    def from_dict(cls, data: dict) -> "Task":
        """从字典恢复"""
        return cls(**data)

第 3 章:持久化层

创建 storage.py

"""JSON 文件存储模块"""
import json
from pathlib import Path
from typing import List

from models import Task


class TaskStorage:
    """任务的 JSON 文件存储"""

    def __init__(self, filepath: str = "tasks.json"):
        self.filepath = Path(filepath)

    def load(self) -> List[Task]:
        """从文件加载所有任务"""
        if not self.filepath.exists():
            return []
        try:
            data = json.loads(self.filepath.read_text(encoding="utf-8"))
            return [Task.from_dict(item) for item in data]
        except (json.JSONDecodeError, KeyError) as e:
            print(f"⚠️  数据文件损坏: {e},将创建新文件")
            return []

    def save(self, tasks: List[Task]) -> None:
        """保存所有任务到文件"""
        data = [task.to_dict() for task in tasks]
        self.filepath.write_text(
            json.dumps(data, ensure_ascii=False, indent=2),
            encoding="utf-8"
        )

第 4 章:业务逻辑

创建 todo_manager.py

"""任务管理业务逻辑"""
from typing import List

from models import Task
from storage import TaskStorage


class TodoManager:
    """待办事项管理器"""

    def __init__(self, storage: TaskStorage):
        self.storage = storage
        self.tasks: List[Task] = self.storage.load()

    def add(self, title: str) -> Task:
        """添加新任务"""
        task = Task(title=title)
        self.tasks.append(task)
        self.storage.save(self.tasks)
        return task

    def list_all(self, show_done: bool = True) -> List[tuple[int, Task]]:
        """列出任务,返回 (序号, 任务) 列表"""
        result = []
        for i, task in enumerate(self.tasks, start=1):
            if show_done or not task.done:
                result.append((i, task))
        return result

    def mark_done(self, index: int) -> Task:
        """将指定任务标记为完成"""
        task = self._get_task(index)
        if task.done:
            raise ValueError(f"任务 {index} 已经完成了")
        task.mark_done()
        self.storage.save(self.tasks)
        return task

    def delete(self, index: int) -> Task:
        """删除指定任务"""
        task = self._get_task(index)
        removed = self.tasks.pop(index - 1)
        self.storage.save(self.tasks)
        return removed

    def _get_task(self, index: int) -> Task:
        """根据用户看到的序号获取任务"""
        if index < 1 or index > len(self.tasks):
            raise IndexError(
                f"任务编号 {index} 无效,当前共 {len(self.tasks)} 个任务"
            )
        return self.tasks[index - 1]

第 5 章:命令行入口

创建 todo.py

#!/usr/bin/env python3
"""命令行待办事项应用 — 入口"""
import sys
from pathlib import Path

from storage import TaskStorage
from todo_manager import TodoManager


DATA_FILE = Path(__file__).parent / "tasks.json"


def print_task(index: int, task, color: bool = True):
    """格式化打印一个任务"""
    status = "✅" if task.done else "⬜"
    done_info = f" (完成于 {task.done_at[:19]})" if task.done else ""
    print(f"  {status} [{index}] {task.title}{done_info}")


def print_usage():
    """打印使用说明"""
    print("""用法:
  python todo.py add <任务内容>      添加新任务
  python todo.py list                列出所有任务
  python todo.py done <编号>         完成任务
  python todo.py delete <编号>       删除任务
  python todo.py help                显示此帮助""")


def main():
    if len(sys.argv) < 2:
        print_usage()
        return

    command = sys.argv[1].lower()
    storage = TaskStorage(str(DATA_FILE))
    manager = TodoManager(storage)

    try:
        if command == "add":
            if len(sys.argv) < 3:
                print("❌ 请提供任务内容")
                return
            title = " ".join(sys.argv[2:])
            task = manager.add(title)
            print(f"✅ 已添加: {task.title}")

        elif command == "list":
            tasks = manager.list_all()
            if not tasks:
                print("📭 暂无任务,用 add 命令添加吧")
                return
            print(f"\n📋 待办事项 (共 {len(tasks)} 项):")
            for i, task in tasks:
                print_task(i, task)

        elif command == "done":
            if len(sys.argv) < 3:
                print("❌ 请提供任务编号")
                return
            index = int(sys.argv[2])
            task = manager.mark_done(index)
            print(f"🎉 已完成: {task.title}")

        elif command == "delete":
            if len(sys.argv) < 3:
                print("❌ 请提供任务编号")
                return
            index = int(sys.argv[2])
            task = manager.delete(index)
            print(f"🗑️  已删除: {task.title}")

        elif command in ("help", "--help", "-h"):
            print_usage()

        else:
            print(f"❌ 未知命令: {command}")
            print_usage()

    except (IndexError, ValueError) as e:
        print(f"❌ 错误: {e}")
    except Exception as e:
        print(f"❌ 意外错误: {e}")
        raise


if __name__ == "__main__":
    main()

第 6 章:运行与测试

# 进入项目目录
cd todo/

# 添加任务
python todo.py add "学习 Python 装饰器"
python todo.py add "写周报"
python todo.py add "买菜"

# 查看列表
python todo.py list

# 完成第一个任务
python todo.py done 1

# 再次查看(观察变化)
python todo.py list

# 删除已完成的任务
python todo.py delete 1

# 最终查看
python todo.py list

预期输出

✅ 已添加: 学习 Python 装饰器
✅ 已添加: 写周报
✅ 已添加: 买菜

📋 待办事项 (共 3 项):
  ⬜ [1] 学习 Python 装饰器
  ⬜ [2] 写周报
  ⬜ [3] 买菜

🎉 已完成: 学习 Python 装饰器

📋 待办事项 (共 3 项):
  ✅ [1] 学习 Python 装饰器 (完成于 2024-...)
  ⬜ [2] 写周报
  ⬜ [3] 买菜

🗑️  已删除: 学习 Python 装饰器

📋 待办事项 (共 2 项):
  ⬜ [1] 写周报
  ⬜ [2] 买菜

第 7 章:扩展练习

试试自己实现以下功能:

  1. 优先级:任务增加 priority 字段(高/中/低),list 按优先级排序
  2. 搜索python todo.py search "Python" 搜索标题包含关键词的任务
  3. 归档:已完成的超过 7 天的任务自动移到 archive.json
  4. 颜色输出:用 colorama 或 ANSI 转义序列让输出更美观
  5. SQLite 存储:将 TaskStorage 替换为 SQLite 实现

提示:使用 argparse 模块替代手动解析 sys.argv 可以让命令解析更专业。


思考题

  1. 如果多个用户同时操作 tasks.json,会发生什么问题?如何解决?
  2. json.dumpsensure_ascii=False 有什么用?
  3. 为什么 DATA_FILEPath(__file__).parent / "tasks.json" 而不是直接 "tasks.json"
  4. 代码中哪些地方用到了「鸭子类型」?

提示:1. 竞态条件 → 文件锁或数据库;2. 保证中文正常显示;3. 确保文件在脚本所在目录,不受运行目录影响;4. TaskStorageTodoManager 之间只依赖接口而非具体实现。

参考资料

  1. [1] Python Software Foundation. Python 官方文档. 2024. https://docs.python.org/3/
  2. [2] Eric Matthes. Python Crash Course (3rd Edition). 2023.
  3. [3] Luciano Ramalho. Fluent Python (2nd Edition). 2022.
  4. [4] Guido van Rossum, Barry Warsaw, Alyssa Coghlan. PEP 8 -- Style Guide for Python Code. 2013. https://peps.python.org/pep-0008/
  5. [5] Python Software Foundation. Python 3.12 Release Notes. 2024. https://docs.python.org/3/whatsnew/3.12.html
  6. [6] Real Python Team. Real Python Tutorials. 2024. https://realpython.com/