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
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" 不是合法整数字符串。应该先 float 再 int。
第 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
思考题
a = [1, 2, 3] 然后 b = a,修改 b[0] = 99,a 会变吗?为什么?
- 什么时候用元组,什么时候用列表?
- 函数参数
def f(a, lst=[]) 有什么陷阱?
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 章:扩展练习
试试自己实现以下功能:
- 优先级:任务增加
priority 字段(高/中/低),list 按优先级排序
- 搜索:
python todo.py search "Python" 搜索标题包含关键词的任务
- 归档:已完成的超过 7 天的任务自动移到
archive.json
- 颜色输出:用
colorama 或 ANSI 转义序列让输出更美观
- SQLite 存储:将
TaskStorage 替换为 SQLite 实现
提示:使用 argparse 模块替代手动解析 sys.argv 可以让命令解析更专业。
思考题
- 如果多个用户同时操作
tasks.json,会发生什么问题?如何解决?
json.dumps 的 ensure_ascii=False 有什么用?
- 为什么
DATA_FILE 用 Path(__file__).parent / "tasks.json" 而不是直接 "tasks.json"?
- 代码中哪些地方用到了「鸭子类型」?
提示:1. 竞态条件 → 文件锁或数据库;2. 保证中文正常显示;3. 确保文件在脚本所在目录,不受运行目录影响;4. TaskStorage 和 TodoManager 之间只依赖接口而非具体实现。