系列文章

Python 从入门到精通

第 36 / 36 篇

从环境安装和基础语法出发,逐步学习工程实践、自动化、数据处理与 Web 开发。

  1. 01
    Python 怎么装、怎么用
  2. 02
    变量、数字和字符串
  3. 03
    列表:把一组数据放在一起
  4. 04
    元组、集合和字典
  5. 05
    条件判断:让程序做选择
  6. 06
    循环:重复的事交给程序
  7. 07
    函数:把代码整理成可复用的块
  8. 08
    模块与包:拆分你的程序
  9. 09
    输入、输出与字符串格式化
  10. 10
    文件读写:保存程序的数据
  11. 11
    异常处理:程序出错时怎么办
  12. 12
    基础阶段练习:命令行记账本
  13. 13
    类和对象:面向对象入门
  14. 14
    继承、组合与特殊方法
  15. 15
    迭代器与生成器
  16. 16
    列表推导式与生成器表达式
  17. 17
    装饰器:给函数增加能力
  18. 18
    上下文管理器与 with
  19. 19
    类型标注与 dataclass
  20. 20
    正则表达式:从文本中找规律
  21. 21
    日期、时间与时区
  22. 22
    日志与调试
  23. 23
    虚拟环境与依赖管理
  24. 24
    测试:让修改不再提心吊胆
  25. 25
    网络请求:用 Python 调用 API
  26. 26
    网页解析与合规采集
  27. 27
    操作 Excel、CSV 与批量文件
  28. 28
    SQLite:给程序加一个数据库
  29. 29
    数据分析入门:NumPy 与 Pandas
  30. 30
    画图:把数据变得直观
  31. 31
    Flask 入门:做一个小网站
  32. 32
    异步编程:同时处理多项任务
  33. 33
    线程、进程与并发选择
  34. 34
    性能分析与优化
  35. 35
    项目结构、配置与发布
  36. 36
    综合项目:从需求到上线正在阅读

查看整个系列 →

最后一篇了。前面 35 篇学的所有东西,这一篇全部串起来:从需求到上线,完整做一个真实项目。做完它,你就正式从”会语法”跨进”会做项目”了。

项目:天气查询小站(命令行 + Web)

需求:一个能查天气的工具,命令行能用,网页也能用,数据存 SQLite 留历史。

技术栈(全是学过的):requests 查天气、sqlite3 存历史、Flask 做网页、pytest 测试、环境变量配配置。

第一步:需求分析(写代码前先想清楚)

功能清单:

  • 输入城市名,返回当前天气(wttr.in 免费 API)
  • 查询记录存 SQLite,能看历史
  • 命令行版 + Flask 网页版
  • 有测试、有 README、能部署

项目结构:

weather_app/
├── src/
│   └── weatherapp/
│       ├── __init__.py
│       ├── weather.py      # 调 API 查天气
│       ├── storage.py      # SQLite 存取
│       ├── cli.py          # 命令行入口
│       └── web.py          # Flask 入口
├── tests/
│   └── test_weather.py
├── requirements.txt
└── README.md

第二步:核心模块——查天气

# src/weatherapp/weather.py
import requests

def fetch_weather(city: str, timeout: int = 10) -> dict:
    """查城市天气,返回结构化数据"""
    resp = requests.get(
        f"https://wttr.in/{city}",
        params={"format": "j1", "lang": "zh"},   # j1 = JSON 格式
        timeout=timeout,
    )
    resp.raise_for_status()
    data = resp.json()
    current = data["current_condition"][0]
    return {
        "city": city,
        "temp": current["temp_C"],
        "feels": current["FeelsLikeC"],
        "humidity": current["humidity"],
        "desc": current["lang_zh"][0]["value"],
    }

if __name__ == "__main__":
    print(fetch_weather("敦化"))

先跑通核心函数——最小可用版本先行,别一上来就搭全套

第三步:存储模块——SQLite 记历史

# src/weatherapp/storage.py
import sqlite3
from datetime import datetime

DB_PATH = "weather.db"

def get_conn():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row     # 按列名取数据
    return conn

def init_db():
    with get_conn() as conn:
        conn.execute("""
            CREATE TABLE IF NOT EXISTS history (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                city TEXT NOT NULL,
                temp TEXT,
                desc TEXT,
                queried_at TEXT
            )
        """)

def save_query(record: dict):
    with get_conn() as conn:
        conn.execute(
            "INSERT INTO history (city, temp, desc, queried_at) VALUES (?, ?, ?, ?)",
            (record["city"], record["temp"], record["desc"],
             datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
        )

def get_history(limit: int = 10) -> list:
    with get_conn() as conn:
        rows = conn.execute(
            "SELECT * FROM history ORDER BY id DESC LIMIT ?", (limit,)
        ).fetchall()
    return [dict(r) for r in rows]

注意 with get_conn() as conn:——正常结束自动 commit,异常自动回滚,不用手动管。

第四步:命令行入口

# src/weatherapp/cli.py
import sys
from .weather import fetch_weather
from .storage import init_db, save_query, get_history

def main():
    init_db()
    if len(sys.argv) < 2:
        print("用法:python -m weatherapp.cli <城市名> 或 history")
        return

    cmd = sys.argv[1]
    if cmd == "history":
        for row in get_history():
            print(f"{row['queried_at']} {row['city']} {row['temp']}°C {row['desc']}")
        return

    try:
        result = fetch_weather(cmd)
        print(f"{result['city']}:{result['temp']}°C,{result['desc']}"
              f"(体感 {result['feels']}°C,湿度 {result['humidity']}%)")
        save_query(result)          # 存历史
    except Exception as e:
        print(f"查询失败:{e}")

if __name__ == "__main__":
    main()

跑起来试试:

python -m weatherapp.cli 敦化
python -m weatherapp.cli history

第五步:Web 版——Flask

# src/weatherapp/web.py
from flask import Flask, request, render_template_string
from .weather import fetch_weather
from .storage import init_db, save_query, get_history

app = Flask(__name__)
init_db()

PAGE = """
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>天气小站</title></head>
<body>
<h1>天气查询</h1>
<form method="post">
    <input name="city" placeholder="城市名">
    <button>查询</button>
</form>
{% if result %}<p>{{ result }}</p>{% endif %}
<h2>最近查询</h2>
<ul>{% for r in history %}<li>{{ r.queried_at }} {{ r.city }} {{ r.temp }}°C {{ r.desc }}</li>{% endfor %}</ul>
</body></html>
"""

@app.route("/", methods=["GET", "POST"])
def index():
    result = None
    if request.method == "POST":
        city = request.form.get("city", "").strip()
        if city:
            try:
                data = fetch_weather(city)
                result = f"{data['city']}:{data['temp']}°C,{data['desc']}"
                save_query(data)
            except Exception as e:
                result = f"查询失败:{e}"
    return render_template_string(PAGE, result=result, history=get_history())

if __name__ == "__main__":
    app.run(debug=True)   # 本地开发;上线用 gunicorn,关 debug

第六步:测试

# tests/test_weather.py
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

from weatherapp import weather, storage

def test_fetch_weather_shape():
    """天气数据应该包含关键字段(打真实 API,注意频率)"""
    data = weather.fetch_weather("敦化")
    assert "city" in data and "temp" in data and "desc" in data

def test_storage_roundtrip():
    """存进去能查出来"""
    storage.init_db()
    storage.save_query({"city": "测试市", "temp": "20", "desc": "晴"})
    history = storage.get_history()
    assert history[0]["city"] == "测试市"

def test_history_limit():
    storage.init_db()
    assert len(storage.get_history(limit=2)) <= 2
pip install -r requirements.txt pytest
pytest

第七步:部署上线

# 服务器上
git clone <你的仓库地址>
cd weather_app
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
pytest                                   # 测试过了再上

gunicorn -w 2 -b 127.0.0.1:8000 "weatherapp.web:app"
# nginx 配好域名转发 + HTTPS,完成!

全系列回顾:你走过的路

回头看这 36 篇,你已经掌握了一个完整的技能树:

  • 基础:变量、容器、条件、循环、函数、模块、文件、异常
  • 进阶:面向对象、迭代器、装饰器、正则、日期、日志、测试
  • 实战:网络请求、网页解析、Excel、SQLite、数据分析、画图、Flask
  • 深入:异步、并发、性能、工程化、部署

这套技能足够你:写自动化脚本、做数据分析、搭个人网站、接 API 做小工具。更重要的是,你学会了怎么学:先跑通、再完善、遇到问题查文档、写测试兜底。

下一步做什么

最好的学习是动手。几个方向:

  • 把记账本升级成 Web 版,部署上线给朋友用
  • 做一个自动备份脚本:定时把重要文件打包上传
  • 抓取一个你常看的网站,做数据分析和可视化
  • 给这个小站(南山小站)写一个 Python 工具:比如批量生成文章摘要

挑一个最感兴趣的,立刻开始。遇到问题很正常——查文档、看报错、拆解问题,你已经全会了。

练习

  1. 给天气小站加一个”删除历史”功能(Web 版加个按钮,storage 加 delete 函数)
  2. 加一个功能:连续查询 3 个城市,输出对比表格
  3. 思考题(收官):回顾整个系列,你觉得哪一篇对你最有价值?把这一篇的内容用自己的话讲给一个完全不懂编程的朋友听——讲得明白,才是真学会了