📖 基于《Python编程:从入门到实践》第3版

Python 从入门到实践

15节课 × 15个小项目,从变量到Web应用,每一步都动手写代码

15
课程
15
项目
~50
代码示例
第 1 课

变量与字符串

📖 教材第1-2章⭐ 入门⏱ 约1小时

学什么

写 Python 程序的第一步:怎么存数据(变量)、怎么处理文字(字符串)、怎么做加减乘除(数字)。

变量
给数据起名字,name = "小明"
字符串
文字数据,用引号包裹,可以拼接、截取、格式化
数字
整数、浮点数,支持加减乘除、乘方、取余
f-string
最常用的字符串格式化:f"你好{name}"

代码

# ── 变量:给数据起一个名字 ──
name = "小明"
age = 25
height = 1.75
is_student = True          # 布尔值:True 或 False

# ── 字符串操作 ──
greeting = "你好, " + name     # 拼接字符串
shout = name.upper()           # 转大写
clean = "  hello  ".strip() # 去掉首尾空格

# ── f-string:最常用的格式化方式 ──
print(f"{name}今年{age}岁,身高{height}米")

# ── 数字运算 ──
total = 10 + 5 * 2     # 20(先乘除后加减)
power = 2 ** 10          # 1024(2的10次方)
remainder = 17 % 5      # 2(取余数:17除以5余2)
rounded = round(3.14159, 2)  # 3.14(保留两位小数)

# ── 类型转换 ──
num_str = str(100)        # 数字 → 字符串 "100"
num = int("42")           # 字符串 → 数字 42
Python 变量不需要声明类型,直接写 name = "小明" 就行。类型是自动推断的。

小项目:个人名片生成器

输入你的信息,自动排版成一张漂亮的名片

print("╔══════════════════╗")
print("║   📇 名片生成器  ║")
print("╚══════════════════╝")

name  = input("姓名: ")
age   = input("年龄: ")
city  = input("城市: ")
job   = input("职业: ")
phone = input("电话: ")

# 计算名片边框宽度(取最长行的长度)
line1 = f"  👤 {name}"
line2 = f"  📍 {city}"
line3 = f"  💼 {job}"
line4 = f"  📞 {phone}"
width = max(len(line1), len(line2), len(line3), len(line4)) + 4

print("╔" + "═" * width + "╗")
print(f"║ {name+' ':{width}}║")
print(f"║ {city+' ':{width}}║")
print(f"║ {job+' ':{width}}║")
print(f"║ {phone+' ':{width}}║")
print("╚" + "═" * width + "╝")
容易出错的地方:input() 返回的永远是字符串。如果需要数字,要手动 int() 转换,但本课先用不着。
第 2 课

列表基础

📖 教材第3章⭐ 入门⏱ 约1小时

学什么

列表是装数据的"篮子"。一个列表可以存很多个东西——商品名、学生分数、待办事项,想存什么存什么。

创建列表
items = ["苹果", "香蕉"]
索引访问
items[0] 是第一个,items[-1] 是最后一个
增删元素
append() 添加、remove() 删除、pop() 弹出
排序
sort() 永久排序、sorted() 临时排序
# ── 创建列表 ──
fruits = ["苹果", "香蕉", "橘子", "葡萄"]
numbers = [3, 1, 4, 1, 5, 9]

# ── 索引(从0开始数) ──
first = fruits[0]      # "苹果"
second = fruits[1]     # "香蕉"
last = fruits[-1]      # "葡萄"(-1 = 最后一个,-2 = 倒数第二个)

# ── 增加元素 ──
fruits.append("西瓜")          # 加到末尾
fruits.insert(1, "芒果")       # 插入到索引1的位置

# ── 删除元素 ──
fruits.remove("香蕉")         # 按值删除(只删第一个匹配的)
gone = fruits.pop()            # 弹出最后一个,可以拿到被删的值
gone2 = fruits.pop(0)        # 弹出索引0的元素
del fruits[1]                 # 直接删除索引1,拿不到值

# ── 排序 ──
numbers.sort()                 # 从小到大,永久改变
numbers.sort(reverse=True)   # 从大到小
temp_sorted = sorted(fruits)   # 临时排序,原列表不变

# ── 其他常用操作 ──
count = len(fruits)           # 列表长度
fruits.reverse()               # 反转顺序
索引为什么从0开始? 因为 Python(和大多数编程语言)内部用"偏移量"定位——第一个元素离起点0步,所以索引是0。

小项目:购物清单管理器

一个命令行工具:添加、查看、标记已买、删除商品

shopping_list = []
while True:
    print("\n📋 购物清单")
    print("  add 添加 | show 查看 | done 完成 | del 删除 | quit 退出")
    cmd = input("> ").strip().lower()

    if cmd == "quit":
        print("👋 再见!")
        break

    elif cmd == "show":
        if not shopping_list:
            print("  清单是空的")
        else:
            for i, item in enumerate(shopping_list, 1):
                print(f"  {i}. {item}")

    elif cmd == "add":
        item = input("  商品名: ")
        shopping_list.append(item)
        print(f"  ✅ 已添加: {item}")

    elif cmd == "done":
        num = int(input("  第几个买好了? "))
        if 1 <= num <= len(shopping_list):
            item = shopping_list.pop(num - 1)
            print(f"  ✅ {item} 已购买")

    elif cmd == "del":
        item = input("  删除什么? ")
        if item in shopping_list:
            shopping_list.remove(item)
            print(f"  🗑 已删除: {item}")
        else:
            print("  ❌ 清单里没有这个")
第 3 课

列表操作与遍历

📖 教材第4章⭐⭐ 基础⏱ 约1.5小时

学什么

一个列表有100个元素,你不可能手动处理每一个。for 循环让你"遍历"列表——对每个元素做同样的事。切片让你取列表的一部分。元组是"不可修改的列表"。

for 循环
for item in list: 逐个处理
range()
生成数字序列,range(1,6) → 1,2,3,4,5
列表切片
取列表一部分:nums[0:3] → 前三个
元组
不可变的列表,用 () 表示
# ── for 循环:对列表里每个元素做同样的事 ──
students = ["小明", "小红", "小刚", "小美"]
for name in students:
    print(f"👋 你好,{name}!")

# ── range():生成一串数字 ──
for i in range(1, 6):       # 1, 2, 3, 4, 5(不含6)
    print(f"第{i}次")

for i in range(5):          # 0, 1, 2, 3, 4(默认从0开始)
    print(i)

# ── enumerate():同时拿索引和值 ──
for index, name in enumerate(students, start=1):
    print(f"{index}号: {name}")

# ── 切片:取列表的"一段" ──
nums = [10, 20, 30, 40, 50, 60]
nums[0:3]    # [10, 20, 30] — 前三个
nums[:3]      # 同上(省略0)
nums[2:]      # [30, 40, 50, 60] — 从索引2到最后
nums[-2:]     # [50, 60] — 最后两个
nums[:]       # 复制整个列表(重要!)

# ── 列表推导式:一行生成列表 ──
squares = [x**2 for x in range(1, 6)]  # [1, 4, 9, 16, 25]
evens = [x for x in range(10) if x % 2 == 0]  # [0,2,4,6,8]

# ── 元组:不能修改的"列表" ──
dimensions = (1920, 1080)    # 屏幕分辨率,不会变
# dimensions[0] = 1280  ❌ 报错!元组不能改

小项目:成绩分析器

输入一批学生成绩,自动排序、统计最高分、最低分、平均分、及格率

scores = []
n = int(input("几个学生? "))

for i in range(n):
    s = float(input(f"第{i+1}个成绩: "))
    scores.append(s)

# 排序(从高到低)
scores.sort(reverse=True)
avg = sum(scores) / len(scores)
passed = sum(1 for s in scores if s >= 60)

print("\n📊 成绩报告")
print(f"  最高分: {max(scores)}")
print(f"  最低分: {min(scores)}")
print(f"  平均分: {avg:.1f}")
print(f"  及格率: {passed}/{n} ({passed/n*100:.0f}%)")
print(f"  排名: {scores}")
第 4 课

if 条件判断

📖 教材第5章⭐⭐ 基础⏱ 约1小时

学什么

程序不能只会一条路走到黑。你需要根据不同的情况做出不同的反应——这就是 if 条件判断。

比较运算符
== != > < >= <=
逻辑运算符
and(与)、or(或)、not(非)
成员运算
in 检查是否在列表中
if-elif-else
多分支判断,只执行一个分支
# ── 基本结构 ──
age = 18

if age < 12:
    print("儿童")
elif age < 18:
    print("青少年")
elif age < 60:
    print("成年人")
else:
    print("老年人")

# ── 多个条件组合 ──
if age >= 18 and is_student:
    print("成年学生")

if vip or amount > 500:
    print("免运费")

# ── 检查列表 ──
banned = ["张三", "李四"]
user = "王五"
if user not in banned:
    print("✅ 可以发言")

# ── 空列表是 False ──
items = []
if items:
    print("有东西")
else:
    print("空的")
if 和 elif 的区别:if 每次都检查,elif 只有前面的条件不成立才检查。多个 if 可能同时执行多个分支,if-elif-else 只会执行一个。

小项目:智能票价计算器

根据年龄和身份自动算票价:老人免费、儿童半价、学生8折、节假日加价

BASE = 100

age = int(input("年龄: "))
is_student = input("学生?(y/n): ") == "y"
is_holiday = input("节假日?(y/n): ") == "y"

if age >= 65:
    rate = 0
    tag = "👴 老人免费"
elif age < 6:
    rate = 0
    tag = "👶 幼儿免费"
elif age < 12:
    rate = 0.5
    tag = "🧒 儿童半价"
elif is_student:
    rate = 0.8
    tag = "🎓 学生8折"
else:
    rate = 1.0
    tag = "全价"

if is_holiday and rate > 0:
    rate += 0.2  # 节假日上浮20%
    tag += " (节假日+20%)"

price = BASE * rate
print(f"\n💰 {tag}")
print(f"   票价: ¥{price:.0f}")
第 5 课

字典

📖 教材第6章⭐⭐ 基础⏱ 约1.5小时

学什么

列表用数字索引找东西("第3个是什么"),字典用名字找东西("小明的电话是多少")。键值对,查找速度极快。

创建字典
{"name":"小明","age":25}
访问安全
dict.get("key","默认值")dict["key"] 安全
遍历
.items() 遍历键值对,.keys() 遍历键
嵌套
字典里放列表、字典里放字典
# ── 创建字典 ──
person = {
    "name": "小明",
    "age": 25,
    "city": "成都",
    "hobbies": ["游泳", "编程", "摄影"]
}

# ── 访问 ──
print(person["name"])                    # 小明
print(person.get("phone", "没留电话"))  # 没留电话(不会报错)

# ── 增删改 ──
person["phone"] = "13800138000"  # 新增键值对
person["age"] = 26               # 修改已有的值
del person["city"]                 # 删除键值对
phone = person.pop("phone")       # 删除并获取值

# ── 遍历字典 ──
for key, value in person.items():
    print(f"{key}: {value}")

# ── 嵌套:字典列表(最常用!) ──
users = [
    {"name": "小明", "score": 85},
    {"name": "小红", "score": 92},
    {"name": "小刚", "score": 78},
]
for user in users:
    print(f"{user['name']}: {user['score']}分")

小项目:智能通讯录

一个完整的通讯录:添加、查找(模糊搜索)、编辑、删除、列出全部

contacts = {}

while True:
    print("\n📒 通讯录")
    cmd = input("1添加 2查找 3编辑 4删除 5列出 6退出\n> ")

    if cmd == "1":
        name = input("姓名: ")
        phone = input("电话: ")
        email = input("邮箱(可选): ")
        contacts[name] = {"phone": phone, "email": email}
        print("✅ 已保存")

    elif cmd == "2":
        keyword = input("搜索: ").lower()
        found = False
        for name, info in contacts.items():
            if keyword in name.lower():
                print(f"  {name}: {info['phone']}  {info['email']}")
                found = True
        if not found:
            print("  ❌ 没找到")

    elif cmd == "3":
        name = input("编辑谁? ")
        if name in contacts:
            new_phone = input(f"新电话({contacts[name]['phone']}): ")
            if new_phone:
                contacts[name]["phone"] = new_phone
            print("✅ 已更新")
        else:
            print("❌ 不存在")

    elif cmd == "4":
        name = input("删除谁? ")
        if contacts.pop(name, None):
            print("✅ 已删除")
        else:
            print("❌ 不存在")

    elif cmd == "5":
        if not contacts:
            print("  通讯录为空")
        for name, info in contacts.items():
            print(f"  📇 {name}: 📞{info['phone']} 📧{info['email']}")

    elif cmd == "6":
        break
第 6 课

用户输入与 while 循环

📖 教材第7章⭐⭐ 基础⏱ 约1小时

学什么

while 是"只要条件成立就一直循环"。结合 input(),你就能做出交互式程序——用户不断输入,程序不断响应。

# ── while 基本用法 ──
count = 0
while count < 5:
    print(f"第{count+1}次")
    count += 1                     # 千万别忘了加,否则死循环!

# ── 用标志控制循环 ──
running = True
while running:
    cmd = input("> ")
    if cmd == "quit":
        running = False          # 改标志,下次循环就结束了
    else:
        print(f"你输入了: {cmd}")

# ── break 和 continue ──
while True:
    text = input("输入(q退出,s跳过): ")
    if text == "q": break        # 立刻跳出循环
    if text == "s": continue     # 跳过本次循环的剩余代码
    print(f"处理: {text}")

小项目:猜数字游戏

经典游戏:电脑出题,玩家猜,提示大了小了,统计尝试次数

import random

print("🎯 猜数字 (1-100)")
print("  输入 0 认输")

secret = random.randint(1, 100)
attempts = 0
hint_shown = False

while True:
    try:
        guess = int(input("\n你猜: "))
    except ValueError:
        print("请输入数字!")
        continue

    attempts += 1

    if guess == 0:
        print(f"答案是 {secret},下次加油!")
        break
    elif guess == secret:
        rating = "🏆" if attempts <= 5 else "👍" if attempts <= 8 else "😅"
        print(f"🎉 {rating} 猜对了! 用了 {attempts} 次")
        break
    elif guess < secret:
        diff = "❄️ 冷" if secret - guess > 30 else "🌤 暖" if secret - guess > 10 else "🔥 热"
        print(f"  {diff} — 太小了!")
    else:
        diff = "❄️ 冷" if guess - secret > 30 else "🌤 暖" if guess - secret > 10 else "🔥 热"
        print(f"  {diff} — 太大了!")

    # 5次后给提示
    if attempts == 5 and not hint_shown:
        hint = "偶数" if secret % 2 == 0 else "奇数"
        print(f  💡 提示: 它是一个{hint}")
        hint_shown = True
第 7 课

函数

📖 教材第8章⭐⭐⭐ 核心⏱ 约1.5小时

学什么

函数是编程最重要的概念之一。把一段代码打包,取名,以后想用就叫它的名字。函数 = 输入(参数)→ 处理 → 输出(返回值)。

# ── 定义函数 ──
def greet(name, greeting="你好"):
    """向某人问好(这是文档字符串)"""
    print(f"{greeting}, {name}!")

greet("小明")                    # 你好, 小明!
greet("小红", "早上好")           # 早上好, 小红!
greet(greeting="晚安", name="小刚") # 关键字参数,顺序不重要

# ── 返回值 ──
def add(a, b):
    return a + b          # return = 把结果"送出去"

result = add(3, 5)            # result = 8

# ── 任意数量参数 *args ──
def make_pizza(size, *toppings):
    print(f"{size}寸披萨, 配料: {toppings}")

make_pizza(12, "芝士", "火腿", "蘑菇")

# ── 任意关键字参数 **kwargs ──
def build_profile(name, **info):
    return {"name": name, **info}

user = build_profile("小明", age=25, city="成都")

小项目:多功能计算器

def add(a, b): return a + b
def sub(a, b): return a - b
def mul(a, b): return a * b
def div(a, b): return a / b if b != 0 else "❌不能除以0"
def power(a, b): return a ** b

ops = {"+": add, "-": sub, "*": mul, "/": div, "^": power}

a = float(input("第一个数: "))
while True:
    op = input("运算符 (+, -, *, /, ^) 或 = 看结果: ")
    if op == "=":
        print(f"📊 结果: {a}")
        break
    if op not in ops:
        print("不认识")
        continue
    b = float(input("下一个数: "))
    a = ops[op](a, b)
    print(f"  = {a}")
第 8 课

函数进阶与模块

📖 教材第8-9章⭐⭐⭐ 核心⏱ 约1.5小时

学什么

Python 自带"工具箱"——标准库。random 生成随机数、datetime 处理时间、json 读写数据。你也可以把自己的函数打包成模块让别人用。

# ── 常用内置模块 ──
import random
import datetime
import json
from math import sqrt, pi  # 只导入需要的

# random
random.randint(1, 100)       # 1-100 随机整数
random.choice(["红","蓝","绿"]) # 随机选一个
random.shuffle(my_list)        # 随机打乱

# datetime
now = datetime.datetime.now()
print(now.strftime("%Y-%m-%d %H:%M"))  # "2026-08-09 14:30"

# json
data = {"name": "小明", "scores": [85, 92]}
text = json.dumps(data, ensure_ascii=False)  # 字典 → JSON字符串
back = json.loads(text)                      # JSON字符串 → 字典

小项目:随机密码生成器

import random
import string

def generate(length=16, use_symbols=True):
    chars = string.ascii_letters + string.digits
    if use_symbols:
        chars += "!@#$%^&*()-_=+"
    return ''.join(random.choice(chars) for _ in range(length))

def strength(pwd):
    score = 0
    if len(pwd) >= 12: score += 1
    if any(c.islower() for c in pwd) and any(c.isupper() for c in pwd): score += 1
    if any(c.isdigit() for c in pwd): score += 1
    if any(c in "!@#$%^&*" for c in pwd): score += 1
    return ["🔴弱","🟡一般","🟢强","🔵很强","🟣超强"][score]

n = int(input("生成几个密码? "))
length = int(input("密码长度(默认16): ") or 16)
for i in range(n):
    p = generate(length)
    print(f"  {i+1}. {p}  [{strength(p)}]")
第 9 课

📖 教材第9章⭐⭐⭐ 核心⏱ 约2小时

类是"蓝图",对象是"实物"。class Dog 定义了狗有什么属性(名字、年龄)和能做什么(叫、坐),my_dog = Dog("旺财",3) 创建一只具体的狗。

class Dog:
    def __init__(self, name, age):
        self.name = name      # 属性:名字
        self.age = age        # 属性:年龄

    def sit(self):              # 方法:坐
        print(f"{self.name} 坐下了")

    def bark(self):             # 方法:叫
        print("汪汪!")

# 创建实例
my_dog = Dog("旺财", 3)
print(my_dog.name)        # 旺财
my_dog.sit()               # 旺财 坐下了

小项目:银行账户系统

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        self.history = []

    def deposit(self, amount):
        self.balance += amount
        self.history.append(f"存入 +¥{amount}")
        return f"✅ 存入 ¥{amount},余额 ¥{self.balance}"

    def withdraw(self, amount):
        if amount > self.balance:
            return f"❌ 余额不足!差 ¥{amount - self.balance}"
        self.balance -= amount
        self.history.append(f"取出 -¥{amount}")
        return f"✅ 取出 ¥{amount},余额 ¥{self.balance}"

    def show(self):
        return f"🏦 {self.owner} 余额: ¥{self.balance}"

acc = BankAccount("小明", 1000)
print(acc.deposit(500))
print(acc.withdraw(2000))
print(acc.withdraw(300))
print(acc.show())
print("交易记录:", acc.history)
第 10 课

继承与多态

📖 教材第9章续⭐⭐⭐⭐ 进阶⏱ 约1.5小时

子类继承父类的所有属性和方法,还可以添加自己的专属功能。多态:不同类有同名方法,但行为不同。

小项目:员工管理系统

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    def info(self):
        return f"{self.name} ¥{self.salary}/月"

class Manager(Employee):
    def __init__(self, name, salary, dept):
        super().__init__(name, salary)
        self.dept = dept; self.team = []
    def add(self, emp): self.team.append(emp)
    def info(self):
        return f"[经理] {super().info()} | {self.dept} | 团队{len(self.team)}人"

class Developer(Employee):
    def __init__(self, name, salary, lang):
        super().__init__(name, salary)
        self.lang = lang
    def info(self):
        return f"[开发] {super().info()} | {self.lang}"

mgr = Manager("老王", 15000, "技术部")
devs = [Developer("小明", 10000, "Python"),
        Developer("小红", 12000, "JS")]
for d in devs: mgr.add(d)
print(mgr.info())
for d in devs: print(d.info())
第 11 课

文件读写与异常处理

📖 教材第10-11章⭐⭐⭐ 核心⏱ 约1.5小时

程序运行时的数据在内存里,关了就没。存到文件里才能永久保留。try-except 让你优雅地处理错误,而不是让程序崩溃。

小项目:日记本

from datetime import datetime

FILE = "diary.txt"

def write():
    text = input("今天想记什么?\n> ")
    now = datetime.now().strftime("%Y-%m-%d %H:%M")
    try:
        with open(FILE, "a", encoding="utf-8") as f:
            f.write(f"[{now}]\n{text}\n\n")
        print("✅ 已保存")
    except Exception as e:
        print(f"❌ 保存失败: {e}")

def read():
    try:
        with open(FILE, "r", encoding="utf-8") as f:
            content = f.read()
        print(content if content else "📭 还没有日记")
    except FileNotFoundError:
        print("📭 还没有日记")

def search(keyword):
    try:
        with open(FILE, "r", encoding="utf-8") as f:
            for line in f:
                if keyword in line:
                    print(line.strip())
    except FileNotFoundError:
        print("📭 还没有日记")

while True:
    cmd = input("\n📔 1写 2看 3搜 4退出\n> ")
    if cmd == "1": write()
    elif cmd == "2": read()
    elif cmd == "3": search(input("关键词: "))
    elif cmd == "4": break
第 12 课

测试

📖 教材第11章续⭐⭐⭐ 核心⏱ 约1小时

改一行代码,怎么确保没搞坏其他功能?测试。写一段代码自动验证你的函数输出对不对。

小项目:为计算器写全套测试

# calculator.py — 被测试的代码
def add(a, b): return a + b
def sub(a, b): return a - b
def mul(a, b): return a * b
def div(a, b):
    if b == 0:
        raise ValueError("除数不能为0")
    return a / b

# test_calc.py — 测试文件(运行: pytest test_calc.py -v)
import pytest
from calculator import add, sub, mul, div

def test_add():
    assert add(2, 3) == 5        # assert = "我断言这个等式成立"
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

def test_div():
    assert div(10, 2) == 5
    assert div(7, 2) == 3.5
    with pytest.raises(ValueError):  # 断言会抛出异常
        div(10, 0)

def test_operations_combined():
    assert mul(3, 4) == 12
    assert sub(add(10, 5), 3) == 12
第 13 课

Pygame 游戏开发

📖 教材第12-14章⭐⭐⭐⭐ 项目⏱ 约3小时

小项目:打砖块 (Breakout)

import pygame

pygame.init()
W, H = 600, 400
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption("🧱 打砖块")
clock = pygame.time.Clock()

paddle = pygame.Rect(W//2-60, H-30, 120, 15)
ball = pygame.Rect(W//2, H//2, 10, 10)
dx, dy = 4, -4
bricks = [pygame.Rect(x*100+5, y*25+5, 90, 20)
          for x in range(6) for y in range(4)]
score = 0

running = True
while running:
    for e in pygame.event.get():
        if e.type == pygame.QUIT: running = False

    paddle.x = pygame.mouse.get_pos()[0] - 60
    ball.x += dx; ball.y += dy
    if ball.left <= 0 or ball.right >= W: dx = -dx
    if ball.top <= 0: dy = -dy
    if ball.colliderect(paddle): dy = -abs(dy)
    if ball.bottom > H: running = False

    hit = False
    for b in bricks[:]:
        if ball.colliderect(b):
            bricks.remove(b); dy = -dy; score += 10
            hit = True; break

    screen.fill((20,20,40))
    pygame.draw.rect(screen, (255,255,255), paddle, border_radius=5)
    pygame.draw.ellipse(screen, (255,180,50), ball)
    for b in bricks:
        c = (100,200,255) if b.y < 50 else (80,220,100) if b.y < 100 else (240,180,60)
        pygame.draw.rect(screen, c, b, border_radius=3)
    pygame.display.flip()
    clock.tick(60)

print(f"游戏结束!得分: {score}")
pygame.quit()
第 14 课

数据可视化

📖 教材第15-17章⭐⭐⭐ 项目⏱ 约2小时

小项目:月度消费图表

import matplotlib.pyplot as plt

categories = ["餐饮","购物","交通","住房","娱乐","壹佰"]
amounts = [1200, 800, 300, 1500, 400, 250]
colors = ["#f97316","#3b82f6","#10b981","#8b5cf6","#f59e0b","#ec4899"]

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

ax1.pie(amounts, labels=categories, colors=colors, autopct='%1.1f%%',
        startangle=90, textprops={'fontsize':10})
ax1.set_title("消费占比")

bars = ax2.bar(categories, amounts, color=colors, edgecolor='white', linewidth=0.5)
for bar, amt in zip(bars, amounts):
    ax2.text(bar.get_x()+bar.get_width()/2, bar.get_height()+20,
             f"¥{amt}", ha='center', fontsize=9)
ax2.set_title("消费金额")
ax2.set_xticklabels(categories, rotation=45)

plt.tight_layout()
plt.savefig("expenses.png", dpi=100)
print("✅ 图表已保存 expenses.png")
第 15 课

Web 应用入门

📖 教材第18-20章⭐⭐⭐⭐ 项目⏱ 约3小时

小项目:迷你博客

# mini_blog.py — 运行: pip install flask && python mini_blog.py
from flask import Flask, request, render_template_string
from datetime import datetime

app = Flask(__name__)
posts = []

HTML = """
<!DOCTYPE html>
<html><head><title>迷你博客</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
body{max-width:640px;margin:0 auto;padding:2rem;font-family:system-ui;
background:#faf9f6;color:#333}
h1{color:#f59e0b} article{background:#fff;padding:1.2rem;margin:1rem 0;
border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,.08)}
form *{display:block;width:100%;margin:.5rem 0;padding:10px;border:1px solid #ddd;border-radius:8px}
button{background:#f59e0b;color:#fff;border:none;cursor:pointer;font-size:1rem}
time{color:#999;font-size:.85rem}
</style></head><body>
<h1>📝 迷你博客</h1>
<form method="post">
<input name="title" placeholder="标题" required>
<textarea name="content" placeholder="写点什么..." rows="4"></textarea>
<button>发布</button>
</form>
{% for p in posts|reverse %}
<article>
<h2>{{ p.title }}</h2>
<p>{{ p.content }}</p>
<time>{{ p.time }}</time>
</article>
{% endfor %}
</body></html>
"""

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        posts.append({
            "title": request.form["title"],
            "content": request.form["content"],
            "time": datetime.now().strftime("%Y-%m-%d %H:%M")
        })
    return render_template_string(HTML, posts=posts)

if __name__ == "__main__":
    print("🌐 浏览器打开 http://127.0.0.1:5000")
    app.run(debug=True)