Python 入门完全教程
从零开始,系统学习 Python 编程语言
目录
- 第一章:Python 简介与环境搭建
- 第二章:变量与数据类型
- 第三章:运算符与表达式
- 第四章:控制流语句
- 第五章:函数与模块
- 第六章:数据结构
- 第七章:面向对象编程
- 第八章:文件操作
- 第九章:异常处理
- 第十章:常用标准库
- 第十一章:实战项目
- 第十二章:常见问题与总结
第一章:Python 简介与环境搭建
1.1 Python 是什么
Python 是一门由 Guido van Rossum 于 1991 年创建的高级编程语言。它以简洁优雅的语法著称,强调代码的可读性。Python 支持多种编程范式,包括面向对象编程、函数式编程和过程式编程。
Python 的核心哲学可以总结为:
- 优雅优于丑陋
- 明确优于隐晦
- 简单优于复杂
- 可读性很重要
1.2 Python 的应用领域
Python 被广泛应用于以下领域:
- Web 开发:Django、Flask、FastAPI 等框架
- 数据科学与机器学习:NumPy、Pandas、Scikit-learn、TensorFlow、PyTorch
- 自动化运维:Ansible、SaltStack
- 网络爬虫:Scrapy、BeautifulSoup、Selenium
- 桌面应用:Tkinter、PyQt
- 游戏开发:Pygame
- 科学计算:SciPy、SymPy
1.3 安装 Python
Windows 安装
- 访问官网 https://www.python.org/downloads/
- 下载最新版本的安装包
- 运行安装程序,务必勾选 "Add Python to PATH"
- 点击 "Install Now" 完成安装
macOS 安装
# 使用 Homebrew 安装
brew install python3
# 或者从官网下载安装包
Linux 安装
# Ubuntu/Debian
sudo apt update
sudo apt install python3 python3-pip
# CentOS/RHEL
sudo yum install python3
# Arch Linux
sudo pacman -S python python-pip
1.4 验证安装
# 检查 Python 版本
python3 --version
# 检查 pip 版本
pip3 --version
1.5 选择开发工具
推荐以下开发工具:
- VS Code:轻量级,插件丰富,适合初学者
- PyCharm:专业 Python IDE,功能强大
- Jupyter Notebook:适合数据分析和学习
- IDLE:Python 自带的简单编辑器
1.6 第一个 Python 程序
创建文件 hello.py:
# 这是我的第一个 Python 程序
# 文件名:hello.py
# 使用 print() 函数输出文本
print("Hello, World!")
print("欢迎来到 Python 的世界!")
# 使用 input() 函数获取用户输入
name = input("请输入你的名字:")
print(f"你好,{name}!很高兴认识你!")
运行程序:
python3 hello.py
1.7 Python 交互模式
# 进入 Python 交互模式
python3
# 在交互模式中尝试
>>> 2 + 3
5
>>> print("Hello")
Hello
>>> exit() # 退出交互模式
1.8 注释
# 这是单行注释
"""
这是多行注释
可以写很多行
通常用于文档说明
'''
'''
这也是多行注释
使用三个单引号
效果与三个双引号相同
'''
# 在实际开发中,建议使用以下方式写文档字符串
def greet(name):
"""
向用户打招呼。
参数:
name (str): 用户的名字
返回:
str: 打招呼的消息
"""
return f"Hello, {name}!"
第二章:变量与数据类型
2.1 变量
变量是存储数据的容器。Python 中的变量不需要声明类型,解释器会根据赋值自动推断。
# 变量命名规则
# 1. 只能包含字母、数字和下划线
# 2. 不能以数字开头
# 3. 不能使用 Python 关键字
# 4. 区分大小写
# 合法的变量名
name = "Alice"
age = 25
student_name = "Bob"
_private_var = "private"
MAX_VALUE = 100
# 不合法的变量名(会报错)
# 2name = "error" # 不能以数字开头
# my-name = "error" # 不能包含连字符
# class = "error" # 不能使用关键字
# Python 命名约定
# - 变量名和函数名使用小写字母加下划线(snake_case)
# - 常量使用全大写字母
# - 类名使用驼峰命名法(CamelCase)
# 动态类型特性
x = 10 # x 是整数类型
print(type(x)) # <class 'int'>
x = "hello" # 现在 x 变成了字符串类型
print(type(x)) # <class 'str'>
x = 3.14 # 现在 x 变成了浮点数类型
print(type(x)) # <class 'float'>
2.2 数字类型
# 整数 (int)
a = 10
b = -5
c = 0
big_number = 1_000_000 # 下划线分隔,提高可读性
print(big_number) # 1000000
# 不同进制的整数
binary = 0b1010 # 二进制,等于 10
octal = 0o17 # 八进制,等于 15
hexadecimal = 0xFF # 十六进制,等于 255
print(binary, octal, hexadecimal) # 10 15 255
# 浮点数 (float)
pi = 3.14159
temperature = -20.5
scientific = 1.5e-4 # 科学计数法,等于 0.00015
print(scientific) # 0.00015
# 浮点数精度问题
print(0.1 + 0.2) # 0.30000000000000004
# 解决方案:使用 decimal 模块
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2')) # 0.3
# 复数 (complex)
z = 3 + 4j
print(z.real) # 3.0
print(z.imag) # 4.0
print(abs(z)) # 5.0 (模)
# 布尔值 (bool)
is_student = True
is_graduated = False
print(type(is_student)) # <class 'bool'>
# 布尔值是整数的子类
print(True + True) # 2
print(True * 10) # 10
print(False + 1) # 1
2.3 字符串类型
# 字符串创建方式
s1 = 'Hello' # 单引号
s2 = "World" # 双引号
s3 = '''多行
字符串''' # 三单引号
s4 = """多行
字符串""" # 三双引号
# 包含引号的字符串
s5 = "It's a dog" # 包含单引号
s6 = 'He said "hi"' # 包含双引号
s7 = "He said \"hi\"" # 使用转义字符
# 常用转义字符
print("换行符:第一行\n第二行")
print("制表符:列1\t列2")
print("反斜杠:\\")
print("引号:\"")
# 原始字符串(忽略转义)
print(r"C:\new\folder") # 输出:C:\new\folder
# 字符串是不可变的
s = "Hello"
# s[0] = 'h' # TypeError: 'str' object does not support item assignment
# 字符串索引
s = "Python"
print(s[0]) # P(第一个字符)
print(s[-1]) # n(最后一个字符)
print(s[2]) # t
# 字符串切片
s = "Hello, Python!"
print(s[0:5]) # Hello(从索引0到4)
print(s[7:]) # Python!(从索引7到末尾)
print(s[:5]) # Hello(从开头到索引4)
print(s[-7:-1]) # Python
print(s[::2]) # Hlo yhn(每隔一个字符)
print(s[::-1]) # !nohtyP ,olleH(反转字符串)
# 字符串方法
text = " Hello, Python World! "
print(text.strip()) # 去除两端空白
print(text.lstrip()) # 去除左端空白
print(text.rstrip()) # 去除右端空白
print(text.lower()) # 转换为小写
print(text.upper()) # 转换为大写
print(text.title()) # 首字母大写
print(text.capitalize()) # 首字母大写
print(text.swapcase()) # 大小写互换
print(text.find("Python")) # 查找子串位置
print(text.replace("Python", "Java")) # 替换子串
print(text.split(",")) # 分割字符串
print(text.count("l")) # 统计子串出现次数
print(text.startswith(" ")) # 检查开头
print(text.endswith("!")) # 检查结尾
print(text.isdigit()) # 检查是否全为数字
print(text.isalpha()) # 检查是否全为字母
# 字符串格式化
name = "Alice"
age = 25
# 方法1:f-string(推荐,Python 3.6+)
msg1 = f"我叫{name},今年{age}岁"
print(msg1)
# 方法2:format() 方法
msg2 = "我叫{},今年{}岁".format(name, age)
print(msg2)
# 方法3:% 格式化(旧式)
msg3 = "我叫%s,今年%d岁" % (name, age)
print(msg3)
# f-string 高级用法
import math
print(f"圆周率是 {math.pi:.2f}") # 保留两位小数
print(f"二进制:{42:b}") # 二进制表示
print(f"十六进制:{255:x}") # 十六进制表示
print(f"科学计数法:{1234567:e}") # 科学计数法
print(f"百分比:{0.85:.1%}") # 百分比
print(f"右对齐:{'hello':>20}") # 右对齐
print(f"左对齐:{'hello':<20}") # 左对齐
print(f"居中:{'hello':^20}") # 居中
print(f"填充:{'hello':*^20}") # 用*填充居中
2.4 类型转换
# 隐式类型转换
a = 10 # int
b = 3.14 # float
c = a + b # 自动转换为 float
print(c, type(c)) # 13.14 <class 'float'>
# 显式类型转换
# int() 转换为整数
print(int(3.9)) # 3(截断,不是四舍五入)
print(int("123")) # 123
print(int(True)) # 1
print(int(False)) # 0
# float() 转换为浮点数
print(float(10)) # 10.0
print(float("3.14")) # 3.14
# str() 转换为字符串
print(str(123)) # "123"
print(str(3.14)) # "3.14"
print(str(True)) # "True"
# bool() 转换为布尔值
# 以下值被视为 False
print(bool(0)) # False
print(bool(0.0)) # False
print(bool("")) # False
print(bool([])) # False
print(bool(None)) # False
# 其他值被视为 True
print(bool(1)) # True
print(bool("hello")) # True
print(bool([1, 2])) # True
# 类型检查
x = 42
print(type(x)) # <class 'int'>
print(isinstance(x, int)) # True
print(isinstance(x, float)) # False
第三章:运算符与表达式
3.1 算术运算符
# 基本算术运算符
a = 15
b = 4
print(a + b) # 加法:19
print(a - b) # 减法:11
print(a * b) # 乘法:60
print(a / b) # 除法:3.75(结果为浮点数)
print(a // b) # 整除:3(向下取整)
print(a % b) # 取余:3
print(a ** b) # 幂运算:50625(15的4次方)
# 注意负数的整除
print(-7 // 2) # -4(向下取整)
print(-7 % 2) # 1(余数为正)
# 增量赋值运算符
x = 10
x += 5 # 等价于 x = x + 5,结果 15
x -= 3 # 等价于 x = x - 3,结果 12
x *= 2 # 等价于 x = x * 2,结果 24
x /= 4 # 等价于 x = x / 4,结果 6.0
x //= 2 # 等价于 x = x // 2,结果 3.0
x **= 3 # 等价于 x = x ** 3,结果 27.0
3.2 比较运算符
# 比较运算符返回布尔值
a = 10
b = 20
print(a == b) # 等于:False
print(a != b) # 不等于:True
print(a > b) # 大于:False
print(a < b) # 小于:True
print(a >= b) # 大于等于:False
print(a <= b) # 小于等于:True
# 链式比较
x = 5
print(1 < x < 10) # True
print(1 < x < 3) # False
print(1 < x > 3 < 10) # True
# 字符串比较(按字典序)
print("apple" < "banana") # True
print("abc" < "abd") # True
print("abc" < "ab") # False
3.3 逻辑运算符
# and、or、not
x = True
y = False
print(x and y) # False(两个都为True才为True)
print(x or y) # True(至少一个为True就为True)
print(not x) # False(取反)
# 短路求值
# and:第一个为False,不计算第二个
# or:第一个为True,不计算第二个
# 实际应用
age = 25
income = 5000
if age >= 18 and income >= 3000:
print("符合贷款条件")
# 逻辑运算符返回的是操作数,不一定是布尔值
print(0 or "hello") # "hello"
print("hello" and "world") # "world"
print(0 and "hello") # 0
print("" or "default") # "default"
3.4 位运算符
# 位运算符
a = 0b1100 # 12
b = 0b1010 # 10
print(bin(a & b)) # 按位与:0b1000 (8)
print(bin(a | b)) # 按位或:0b1110 (14)
print(bin(a ^ b)) # 按位异或:0b0110 (6)
print(bin(~a)) # 按位取反:-0b1101 (-13)
print(bin(a << 2)) # 左移:0b110000 (48)
print(bin(a >> 2)) # 右移:0b11 (3)
# 实际应用:权限系统
READ = 0b100 # 4
WRITE = 0b010 # 2
EXECUTE = 0b001 # 1
# 设置权限
permission = READ | WRITE # 读写权限
print(bin(permission)) # 0b110
# 检查权限
has_read = bool(permission & READ)
has_execute = bool(permission & EXECUTE)
print(f"有读权限:{has_read}") # True
print(f"有执行权限:{has_execute}") # False
3.5 成员运算符和身份运算符
# 成员运算符:in, not in
fruits = ["apple", "banana", "cherry"]
print("apple" in fruits) # True
print("grape" not in fruits) # True
text = "Hello, Python"
print("Python" in text) # True
# 身份运算符:is, is not
# is 比较的是内存地址(id),== 比较的是值
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True(值相同)
print(a is b) # False(不是同一个对象)
print(a is c) # True(是同一个对象)
print(id(a), id(b), id(c))
# 小整数池(-5 到 256)和字符串驻留
x = 256
y = 256
print(x is y) # True(小整数池)
x = 257
y = 257
print(x is y) # False(超出小整数池,但可能因实现而异)
# None 的比较
x = None
print(x is None) # 推荐写法
print(x == None) # 也可以,但不推荐
3.6 运算符优先级
# 优先级从高到低:
# 1. **(幂运算)
# 2. +x, -x, ~x(一元运算符)
# 3. *, /, //, %
# 4. +, -
# 5. <<, >>
# 6. &
# 7. ^
# 8. |
# 9. ==, !=, <, <=, >, >=, is, in
# 10. not
# 11. and
# 12. or
# 示例
result = 2 + 3 * 4 ** 2
# 先算 4**2 = 16,再算 3*16 = 48,最后 2+48 = 50
print(result) # 50
# 使用括号改变优先级
result = (2 + 3) * 4 ** 2
print(result) # 80
第四章:控制流语句
4.1 条件语句
# if 语句
age = 18
if age >= 18:
print("你已经成年了")
# if-else 语句
age = 15
if age >= 18:
print("成年人")
else:
print("未成年人")
# if-elif-else 语句
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
else:
grade = "F"
print(f"成绩等级:{grade}")
# 三元表达式(条件表达式)
age = 20
status = "成年" if age >= 18 else "未成年"
print(status)
# 嵌套条件
num = 15
if num > 0:
if num % 2 == 0:
print("正偶数")
else:
print("正奇数")
elif num < 0:
print("负数")
else:
print("零")
# match-case(Python 3.10+)
command = "start"
match command:
case "start":
print("启动程序")
case "stop":
print("停止程序")
case "restart":
print("重启程序")
case _:
print("未知命令")
4.2 for 循环
# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(f"我喜欢{fruit}")
# 使用 range()
# range(stop) - 从0到stop-1
for i in range(5):
print(i) # 0, 1, 2, 3, 4
# range(start, stop) - 从start到stop-1
for i in range(2, 6):
print(i) # 2, 3, 4, 5
# range(start, stop, step)
for i in range(0, 10, 2):
print(i) # 0, 2, 4, 6, 8
# 倒序
for i in range(10, 0, -1):
print(i) # 10, 9, 8, ..., 1
# enumerate() - 同时获取索引和值
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# enumerate 设置起始索引
for index, fruit in enumerate(fruits, start=1):
print(f"{index}: {fruit}")
# zip() - 并行遍历多个序列
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name} 今年 {age} 岁")
# 遍历字典
person = {"name": "Alice", "age": 25, "city": "Beijing"}
# 遍历键
for key in person:
print(key)
# 遍历值
for value in person.values():
print(value)
# 遍历键值对
for key, value in person.items():
print(f"{key}: {value}")
# 列表推导式
squares = [x ** 2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 带条件的列表推导式
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]
# 嵌套列表推导式
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 字典推导式
squares_dict = {x: x ** 2 for x in range(5)}
print(squares_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 集合推导式
unique_lengths = {len(word) for word in ["hello", "world", "hi", "hey"]}
print(unique_lengths) # {2, 3, 5}
4.3 while 循环
# 基本 while 循环
count = 0
while count < 5:
print(count)
count += 1
# 用户输入验证
while True:
user_input = input("请输入一个正整数:")
if user_input.isdigit() and int(user_input) > 0:
number = int(user_input)
break
print("输入无效,请重试")
print(f"你输入的是:{number}")
# while-else
count = 0
while count < 5:
print(count)
count += 1
else:
print("循环正常结束")
4.4 循环控制
# break - 跳出循环
for i in range(10):
if i == 5:
break
print(i) # 0, 1, 2, 3, 4
# continue - 跳过当前迭代
for i in range(10):
if i % 2 == 0:
continue
print(i) # 1, 3, 5, 7, 9
# pass - 占位符
for i in range(10):
if i % 2 == 0:
pass # 暂时什么都不做,以后再实现
else:
print(i)
# 嵌套循环与标签(Python没有goto,用函数模拟)
def search_in_matrix(matrix, target):
for i, row in enumerate(matrix):
for j, val in enumerate(row):
if val == target:
return i, j
return -1, -1
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(search_in_matrix(matrix, 5)) # (1, 1)
4.5 综合练习
# 练习1:九九乘法表
for i in range(1, 10):
for j in range(1, i + 1):
print(f"{j}×{i}={i*j}", end="\t")
print()
# 练习2:判断素数
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
# 打印100以内的素数
primes = [n for n in range(2, 100) if is_prime(n)]
print(primes)
# 练习3:猜数字游戏
import random
secret = random.randint(1, 100)
attempts = 0
print("欢迎来到猜数字游戏!")
print("我已经想好了一个1到100之间的数字。")
while True:
guess = int(input("请输入你的猜测:"))
attempts += 1
if guess < secret:
print("太小了!")
elif guess > secret:
print("太大了!")
else:
print(f"恭喜你!猜对了!答案就是 {secret}")
print(f"你一共猜了 {attempts} 次")
break
第五章:函数与模块
5.1 函数定义与调用
# 基本函数定义
def greet():
print("Hello, World!")
greet() # 调用函数
# 带参数的函数
def greet_name(name):
print(f"Hello, {name}!")
greet_name("Alice")
# 带返回值的函数
def add(a, b):
return a + b
result = add(3, 5)
print(result) # 8
# 多个返回值
def get_dimensions():
return 1920, 1080
width, height = get_dimensions()
print(f"分辨率:{width}x{height}")
# 默认参数
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
# 可变参数 *args
def sum_all(*args):
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
# 关键字可变参数 **kwargs
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="Beijing")
# 混合使用
def func(a, b, *args, **kwargs):
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"kwargs={kwargs}")
func(1, 2, 3, 4, x=5, y=6)
# keyword-only 参数
def func(a, b, *, c, d):
print(a, b, c, d)
func(1, 2, c=3, d=4) # 正确
# func(1, 2, 3, 4) # 错误:c 和 d 必须用关键字传递
5.2 作用域
# 全局变量和局部变量
x = 10 # 全局变量
def func():
x = 20 # 局部变量
print(f"函数内:{x}")
func() # 函数内:20
print(f"函数外:{x}") # 函数外:10
# global 关键字
x = 10
def func():
global x
x = 20
print(f"函数内:{x}")
func() # 函数内:20
print(f"函数外:{x}") # 函数外:20
# nonlocal 关键字
def outer():
x = 10
def inner():
nonlocal x
x = 20
inner()
print(x) # 20
outer()
# LEGB 规则
# L - Local(局部作用域)
# E - Enclosing(嵌套函数的外层作用域)
# G - Global(全局作用域)
# B - Built-in(内置作用域)
5.3 Lambda 表达式
# 基本 lambda
square = lambda x: x ** 2
print(square(5)) # 25
# 多参数
add = lambda x, y: x + y
print(add(3, 5)) # 8
# 在排序中使用
students = [
{"name": "Alice", "grade": 88},
{"name": "Bob", "grade": 95},
{"name": "Charlie", "grade": 72}
]
# 按成绩排序
students.sort(key=lambda s: s["grade"])
print(students)
# 在 map() 中使用
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# 在 filter() 中使用
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even) # [2, 4]
# 在 reduce() 中使用
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
5.4 装饰器
# 简单装饰器
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后
# 带参数的装饰器
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# 保留原函数信息的装饰器
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
"""wrapper 的文档字符串"""
print("装饰器执行")
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""example 的文档字符串"""
pass
print(example.__name__) # example
print(example.__doc__) # example 的文档字符串
# 实用装饰器:计时器
import time
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行耗时:{end - start:.4f}秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "完成"
result = slow_function()
5.5 模块与包
# 创建模块
# 文件:math_utils.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b
PI = 3.14159
# 使用模块
import math_utils
print(math_utils.add(3, 5))
# 从模块导入特定内容
from math_utils import add, PI
print(add(3, 5))
print(PI)
# 导入并重命名
import math_utils as mu
print(mu.add(3, 5))
# 导入所有(不推荐)
from math_utils import *
# 包的结构
# mypackage/
# __init__.py
# module1.py
# module2.py
# subpackage/
# __init__.py
# module3.py
# 常用标准库模块
import os # 操作系统相关
import sys # 系统相关
import datetime # 日期时间
import json # JSON 处理
import re # 正则表达式
import math # 数学函数
import random # 随机数
import collections # 高级数据结构
import itertools # 迭代器工具
import functools # 函数工具
import pathlib # 路径处理
# 模块搜索路径
import sys
print(sys.path)
# 安装第三方模块
# pip install requests
# pip install numpy
# pip install flask
5.6 递归函数
# 阶乘
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120
# 斐波那契数列
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# 带缓存的斐波那契
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
print(fib(50)) # 12586269025
# 二分查找(递归)
def binary_search(arr, target, low=0, high=None):
if high is None:
high = len(arr) - 1
if low > high:
return -1
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
return binary_search(arr, target, mid + 1, high)
else:
return binary_search(arr, target, low, mid - 1)
numbers = [1, 3, 5, 7, 9, 11, 13, 15]
print(binary_search(numbers, 7)) # 3
第六章:数据结构
6.1 列表(List)
# 创建列表
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, [1, 2, 3]]
empty = []
# 访问元素
print(fruits[0]) # apple
print(fruits[-1]) # cherry
# 修改元素
fruits[0] = "grape"
print(fruits) # ['grape', 'banana', 'cherry']
# 列表方法
fruits = ["apple", "banana", "cherry"]
# 添加元素
fruits.append("date") # 末尾添加
fruits.insert(1, "blueberry") # 指定位置插入
fruits.extend(["elderberry", "fig"]) # 扩展列表
# 删除元素
fruits.remove("banana") # 删除指定值
popped = fruits.pop() # 弹出末尾元素
popped = fruits.pop(0) # 弹出指定位置元素
del fruits[0] # 删除指定位置元素
fruits.clear() # 清空列表
# 查找元素
fruits = ["apple", "banana", "cherry", "apple"]
print(fruits.index("apple")) # 0(第一次出现的位置)
print(fruits.count("apple")) # 2(出现次数)
print("banana" in fruits) # True
# 排序
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort() # 原地排序
print(numbers) # [1, 1, 2, 3, 4, 5, 6, 9]
numbers.sort(reverse=True) # 降序排序
print(numbers) # [9, 6, 5, 4, 3, 2, 1, 1]
# sorted() 不修改原列表
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(numbers) # [3, 1, 4, 1, 5]
print(sorted_numbers) # [1, 1, 3, 4, 5]
# 反转
numbers = [1, 2, 3, 4, 5]
numbers.reverse() # 原地反转
print(numbers) # [5, 4, 3, 2, 1]
reversed_numbers = list(reversed(numbers)) # 不修改原列表
# 列表复制
original = [1, 2, 3, [4, 5]]
shallow = original.copy() # 浅拷贝
shallow = original[:] # 浅拷贝
import copy
deep = copy.deepcopy(original) # 深拷贝
original[3].append(6)
print(original) # [1, 2, 3, [4, 5, 6]]
print(shallow) # [1, 2, 3, [4, 5, 6]] 浅拷贝受影响
print(deep) # [1, 2, 3, [4, 5]] 深拷贝不受影响
6.2 元组(Tuple)
# 创建元组
coordinates = (10, 20)
single = (42,) # 单元素元组需要逗号
empty = ()
mixed = (1, "hello", 3.14)
# 元组是不可变的
# coordinates[0] = 15 # TypeError
# 元组操作
print(coordinates[0]) # 10
print(len(coordinates)) # 2
print(10 in coordinates) # True
# 元组解包
x, y = coordinates
print(x, y) # 10 20
# 交换变量
a, b = 1, 2
a, b = b, a
print(a, b) # 2 1
# 元组方法
t = (1, 2, 3, 2, 2)
print(t.count(2)) # 3
print(t.index(3)) # 2
# 命名元组
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y) # 10 20
print(p[0], p[1]) # 10 20
6.3 字典(Dictionary)
# 创建字典
person = {
"name": "Alice",
"age": 25,
"city": "Beijing"
}
# 也可以使用 dict() 构造函数
person = dict(name="Alice", age=25, city="Beijing")
# 访问元素
print(person["name"]) # Alice
print(person.get("email", "N/A")) # N/A(默认值)
# 修改和添加
person["age"] = 26 # 修改
person["email"] = "a@b.com" # 添加
# 删除
del person["email"] # 删除键值对
email = person.pop("email", None) # 弹出(带默认值)
person.popitem() # 弹出最后一个键值对
# 字典方法
person = {"name": "Alice", "age": 25, "city": "Beijing"}
print(person.keys()) # dict_keys(['name', 'age', 'city'])
print(person.values()) # dict_values(['Alice', 25, 'Beijing'])
print(person.items()) # dict_items([('name', 'Alice'), ...])
# 更新字典
person.update({"age": 26, "email": "a@b.com"})
# 合并字典(Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = dict1 | dict2
print(merged) # {'a': 1, 'b': 3, 'c': 4}
# 字典推导式
squares = {x: x**2 for x in range(6)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 嵌套字典
students = {
"Alice": {"age": 20, "grade": 88},
"Bob": {"age": 22, "grade": 95}
}
print(students["Alice"]["grade"]) # 88
# defaultdict
from collections import defaultdict
word_count = defaultdict(int)
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
for word in words:
word_count[word] += 1
print(dict(word_count)) # {'apple': 3, 'banana': 2, 'cherry': 1}
# Counter
from collections import Counter
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counter = Counter(words)
print(counter) # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(counter.most_common(2)) # [('apple', 3), ('banana', 2)]
6.4 集合(Set)
# 创建集合
fruits = {"apple", "banana", "cherry"}
numbers = set([1, 2, 3, 2, 1]) # 自动去重
print(numbers) # {1, 2, 3}
# 空集合(不能用 {},那是空字典)
empty_set = set()
empty_dict = {}
# 集合操作
fruits = {"apple", "banana", "cherry"}
# 添加和删除
fruits.add("date")
fruits.discard("banana") # 删除(不存在不报错)
fruits.remove("cherry") # 删除(不存在会报错)
# 集合运算
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}
print(a | b) # 并集:{1, 2, 3, 4, 5, 6, 7, 8}
print(a & b) # 交集:{4, 5}
print(a - b) # 差集:{1, 2, 3}
print(a ^ b) # 对称差集:{1, 2, 3, 6, 7, 8}
# 集合推导式
squares = {x**2 for x in range(10)}
print(squares) # {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
# frozenset(不可变集合)
frozen = frozenset([1, 2, 3])
# frozen.add(4) # AttributeError
6.5 数据结构选择指南
# 何时使用列表:有序、可重复、需要索引访问
scores = [88, 92, 75, 92, 88]
# 何时使用元组:不可变数据、函数返回值、字典键
point = (10, 20)
rgb_color = (255, 128, 0)
# 何时使用字典:键值对映射、快速查找
user = {"id": 1, "name": "Alice", "email": "a@b.com"}
# 何时使用集合:去重、集合运算、成员测试
unique_visitors = {"user1", "user2", "user3"}
# 性能比较
# 列表查找:O(n)
# 字典查找:O(1)
# 集合查找:O(1)
第七章:面向对象编程
7.1 类与对象
# 定义类
class Dog:
# 类变量(所有实例共享)
species = "Canis familiaris"
# 初始化方法(构造函数)
def __init__(self, name, age):
# 实例变量
self.name = name
self.age = age
# 实例方法
def bark(self):
return f"{self.name} says: Woof!"
def get_info(self):
return f"{self.name} is {self.age} years old"
# 创建对象
dog1 = Dog("Buddy", 3)
dog2 = Dog("Charlie", 5)
print(dog1.bark()) # Buddy says: Woof!
print(dog2.get_info()) # Charlie is 5 years old
print(Dog.species) # Canis familiaris
# 动态添加属性
dog1.color = "Brown"
print(dog1.color) # Brown
7.2 访问控制
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # 公有属性
self._type = "savings" # 保护属性(约定)
self.__balance = balance # 私有属性(名称修饰)
# getter 方法
@property
def balance(self):
return self.__balance
# setter 方法
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError("余额不能为负数")
self.__balance = value
def deposit(self, amount):
if amount <= 0:
raise ValueError("存款金额必须大于0")
self.__balance += amount
def withdraw(self, amount):
if amount > self.__balance:
raise ValueError("余额不足")
self.__balance -= amount
# 使用
account = BankAccount("Alice", 1000)
print(account.balance) # 1000(通过 property 访问)
account.deposit(500)
print(account.balance) # 1500
account.withdraw(200)
print(account.balance) # 1300
# account.__balance # AttributeError(私有属性)
# 但可以通过 _BankAccount__balance 访问(不推荐)
print(account._BankAccount__balance) # 1300
7.3 继承
# 基类
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
def speak(self):
return f"{self.name} says {self.sound}"
def __str__(self):
return f"{self.name} ({self.__class__.__name__})"
# 派生类
class Dog(Animal):
def __init__(self, name):
super().__init__(name, "Woof")
def fetch(self, item):
return f"{self.name} fetches the {item}"
class Cat(Animal):
def __init__(self, name):
super().__init__(name, "Meow")
def purr(self):
return f"{self.name} purrs..."
# 使用
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Buddy says Woof
print(dog.fetch("ball")) # Buddy fetches the ball
print(cat.speak()) # Whiskers says Meow
print(cat.purr()) # Whiskers purrs...
# isinstance 和 issubclass
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True
print(issubclass(Dog, Animal)) # True
# 多继承
class Flyable:
def fly(self):
return f"{self.name} is flying"
class Swimmable:
def swim(self):
return f"{self.name} is swimming"
class Duck(Animal, Flyable, Swimmable):
def __init__(self, name):
super().__init__(name, "Quack")
duck = Duck("Donald")
print(duck.speak()) # Donald says Quack
print(duck.fly()) # Donald is flying
print(duck.swim()) # Donald is swimming
# MRO(方法解析顺序)
print(Duck.__mro__)
7.4 多态
# 多态示例
class Shape:
def area(self):
raise NotImplementedError
def perimeter(self):
raise NotImplementedError
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
def perimeter(self):
import math
return 2 * math.pi * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Triangle(Shape):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def area(self):
s = (self.a + self.b + self.c) / 2
return (s * (s - self.a) * (s - self.b) * (s - self.c)) ** 0.5
def perimeter(self):
return self.a + self.b + self.c
# 多态:同一接口,不同实现
shapes = [Circle(5), Rectangle(4, 6), Triangle(3, 4, 5)]
for shape in shapes:
print(f"{shape.__class__.__name__}:")
print(f" 面积: {shape.area():.2f}")
print(f" 周长: {shape.perimeter():.2f}")
# 鸭子类型
class Duck:
def quack(self):
print("Quack!")
class Person:
def quack(self):
print("I'm quacking like a duck!")
def make_it_quack(thing):
thing.quack()
make_it_quack(Duck()) # Quack!
make_it_quack(Person()) # I'm quacking like a duck!
7.5 魔术方法
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector({self.x!r}, {self.y!r})"
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __len__(self):
return int((self.x**2 + self.y**2)**0.5)
def __bool__(self):
return self.x != 0 or self.y != 0
def __getitem__(self, index):
if index == 0:
return self.x
elif index == 1:
return self.y
raise IndexError("索引超出范围")
# 使用
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1) # Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v1 - v2) # Vector(2, 2)
print(v1 * 3) # Vector(9, 12)
print(v1 == v2) # False
print(len(v1)) # 5
print(bool(v1)) # True
print(v1[0]) # 3
7.6 抽象基类
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def connect(self):
pass
@abstractmethod
def disconnect(self):
pass
@abstractmethod
def query(self, sql):
pass
def execute(self, sql):
"""非抽象方法,有默认实现"""
print(f"执行:{sql}")
return self.query(sql)
class MySQL(Database):
def __init__(self, host, port):
self.host = host
self.port = port
def connect(self):
print(f"连接到 MySQL {self.host}:{self.port}")
def disconnect(self):
print("断开 MySQL 连接")
def query(self, sql):
print(f"执行 MySQL 查询:{sql}")
return []
# db = Database() # TypeError: 不能实例化抽象类
db = MySQL("localhost", 3306)
db.connect()
db.execute("SELECT * FROM users")
db.disconnect()
7.7 类方法和静态方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
@classmethod
from_string(cls, date_string):
"""类方法:从字符串创建 Date 对象"""
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day)
@staticmethod
def is_valid_date(year, month, day):
"""静态方法:验证日期是否有效"""
if month < 1 or month > 12:
return False
if day < 1 or day > 31:
return False
return True
# 使用
date1 = Date(2024, 1, 15)
date2 = Date.from_string("2024-03-20")
print(date1) # 2024-01-15
print(date2) # 2024-03-20
print(Date.is_valid_date(2024, 2, 29)) # True
print(Date.is_valid_date(2024, 13, 1)) # False
7.8 组合与聚合
# 组合:部分与整体共存亡
class Engine:
def __init__(self, horsepower):
self.horsepower = horsepower
def start(self):
return f"引擎启动 ({self.horsepower}HP)"
class Car:
def __init__(self, brand, horsepower):
self.brand = brand
self.engine = Engine(horsepower) # 组合
def start(self):
return f"{self.brand}: {self.engine.start()}"
car = Car("Toyota", 150)
print(car.start()) # Toyota: 引擎启动 (150HP)
# 聚合:部分可以独立存在
class Team:
def __init__(self, name):
self.name = name
self.members = []
def add_member(self, member):
self.members.append(member)
def show_members(self):
return [str(m) for m in self.members]
class Player:
def __init__(self, name, number):
self.name = name
self.number = number
def __str__(self):
return f"#{self.number} {self.name}"
team = Team("Dream Team")
p1 = Player("Alice", 10)
p2 = Player("Bob", 7)
team.add_member(p1)
team.add_member(p2)
print(team.show_members()) # ['#10 Alice', '#7 Bob']
第八章:文件操作
8.1 文本文件操作
# 写入文件
# 使用 with 语句确保文件正确关闭
with open("example.txt", "w", encoding="utf-8") as f:
f.write("Hello, World!\n")
f.write("这是第二行\n")
f.write("这是第三行\n")
# 读取文件
with open("example.txt", "r", encoding="utf-8") as f:
content = f.read()
print(content)
# 逐行读取
with open("example.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip()) # strip() 去除换行符
# 读取所有行到列表
with open("example.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
print(lines) # ['Hello, World!\n', '这是第二行\n', '这是第三行\n']
# 追加内容
with open("example.txt", "a", encoding="utf-8") as f:
f.write("这是追加的内容\n")
# 文件模式
# 'r' - 只读(默认)
# 'w' - 写入(覆盖)
# 'a' - 追加
# 'x' - 创建(文件已存在则报错)
# 'r+' - 读写
# 'w+' - 读写(覆盖)
# 'a+' - 读写(追加)
# 'rb' - 二进制读
# 'wb' - 二进制写
# 文件位置操作
with open("example.txt", "r", encoding="utf-8") as f:
print(f.tell()) # 0(当前位置)
f.read(5)
print(f.tell()) # 5
f.seek(0) # 回到开头
print(f.tell()) # 0
8.2 二进制文件操作
# 写入二进制文件
data = b'\x00\x01\x02\x03\x04'
with open("binary.dat", "wb") as f:
f.write(data)
# 读取二进制文件
with open("binary.dat", "rb") as f:
data = f.read()
print(data) # b'\x00\x01\x02\x03\x04'
print(list(data)) # [0, 1, 2, 3, 4]
# 使用 struct 处理二进制数据
import struct
# 打包数据
data = struct.pack('if', 42, 3.14) # int 和 float
print(data)
# 解包数据
num, value = struct.unpack('if', data)
print(num, value) # 42 3.140000104904175
8.3 CSV 文件操作
import csv
# 写入 CSV
data = [
["Name", "Age", "City"],
["Alice", 25, "Beijing"],
["Bob", 30, "Shanghai"],
["Charlie", 35, "Guangzhou"]
]
with open("people.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(data)
# 读取 CSV
with open("people.csv", "r", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)
# 使用字典方式读写
with open("people_dict.csv", "w", newline="", encoding="utf-8") as f:
fieldnames = ["Name", "Age", "City"]
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({"Name": "Alice", "Age": 25, "City": "Beijing"})
writer.writerow({"Name": "Bob", "Age": 30, "City": "Shanghai"})
with open("people_dict.csv", "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
print(dict(row))
8.4 JSON 文件操作
import json
# Python 对象转 JSON
data = {
"name": "Alice",
"age": 25,
"hobbies": ["reading", "coding", "hiking"],
"address": {
"city": "Beijing",
"zip": "100000"
}
}
# 写入 JSON 文件
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 读取 JSON 文件
with open("data.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded)
# 字符串转换
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)
parsed = json.loads(json_str)
print(parsed)
8.5 pathlib 模块
from pathlib import Path
# 创建路径对象
p = Path(".")
home = Path.home()
project = Path("/home/user/project")
# 路径操作
file_path = project / "src" / "main.py"
print(file_path) # /home/user/project/src/main.py
# 路径属性
p = Path("/home/user/document.txt")
print(p.name) # document.txt
print(p.stem) # document
print(p.suffix) # .txt
print(p.parent) # /home/user
print(p.parts) # ('/', 'home', 'user', 'document.txt')
# 文件操作
p = Path("test.txt")
p.write_text("Hello, World!", encoding="utf-8")
content = p.read_text(encoding="utf-8")
print(content)
# 目录操作
new_dir = Path("new_directory")
new_dir.mkdir(exist_ok=True)
# 遍历目录
for item in Path(".").iterdir():
print(item)
# 查找文件
for py_file in Path(".").glob("*.py"):
print(py_file)
# 递归查找
for py_file in Path(".").rglob("*.py"):
print(py_file)
# 文件信息
p = Path("test.txt")
print(p.exists()) # True
print(p.is_file()) # True
print(p.is_dir()) # False
print(p.stat().st_size) # 文件大小
8.6 os 模块
import os
# 当前工作目录
print(os.getcwd())
# 改变目录
# os.chdir("/tmp")
# 列出目录内容
print(os.listdir("."))
# 创建目录
os.makedirs("new/path", exist_ok=True)
# 删除文件
# os.remove("file.txt")
# 删除目录
# os.rmdir("empty_dir")
# 重命名
# os.rename("old.txt", "new.txt")
# 环境变量
print(os.environ.get("HOME"))
print(os.getenv("PATH"))
# 路径操作
print(os.path.join("home", "user", "file.txt"))
print(os.path.exists("file.txt"))
print(os.path.isfile("file.txt"))
print(os.path.isdir("directory"))
print(os.path.basename("/home/user/file.txt")) # file.txt
print(os.path.dirname("/home/user/file.txt")) # /home/user
print(os.path.splitext("file.txt")) # ('file', '.txt')
第九章:异常处理
9.1 基本异常处理
# try-except
try:
result = 10 / 0
except ZeroDivisionError:
print("不能除以零!")
# 捕获多个异常
try:
num = int(input("请输入数字:"))
result = 10 / num
except ValueError:
print("输入的不是有效数字!")
except ZeroDivisionError:
print("不能除以零!")
# 捕获多个异常(合并)
try:
num = int(input("请输入数字:"))
result = 10 / num
except (ValueError, ZeroDivisionError) as e:
print(f"发生错误:{e}")
# 获取异常信息
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"错误类型:{type(e).__name__}")
print(f"错误信息:{e}")
# try-except-else
try:
num = int(input("请输入数字:"))
result = 10 / num
except ValueError:
print("输入无效")
except ZeroDivisionError:
print("不能除以零")
else:
print(f"结果是:{result}") # 没有异常时执行
# try-except-finally
try:
f = open("file.txt", "r")
content = f.read()
except FileNotFoundError:
print("文件不存在")
finally:
print("清理工作...") # 无论如何都会执行
9.2 常见内置异常
# TypeError - 类型错误
# try:
# result = "hello" + 42
# except TypeError as e:
# print(e)
# ValueError - 值错误
# try:
# num = int("abc")
# except ValueError as e:
# print(e)
# IndexError - 索引错误
# try:
# lst = [1, 2, 3]
# print(lst[10])
# except IndexError as e:
# print(e)
# KeyError - 键错误
# try:
# d = {"a": 1}
# print(d["b"])
# except KeyError as e:
# print(e)
# AttributeError - 属性错误
# try:
# s = "hello"
# s.append("x")
# except AttributeError as e:
# print(e)
# FileNotFoundError - 文件未找到
# try:
# f = open("nonexistent.txt")
# except FileNotFoundError as e:
# print(e)
# ImportError - 导入错误
# try:
# import nonexistent_module
# except ImportError as e:
# print(e)
# Exception 是所有内置异常的基类
try:
# 某些可能出错的代码
pass
except Exception as e:
print(f"捕获到异常:{e}")
9.3 自定义异常
# 自定义异常类
class CustomError(Exception):
"""自定义异常基类"""
pass
class ValidationError(CustomError):
"""验证错误"""
def __init__(self, field, message):
self.field = field
self.message = message
super().__init__(f"验证失败 - {field}: {message}")
class NotFoundError(CustomError):
"""资源未找到"""
def __init__(self, resource, identifier):
self.resource = resource
self.identifier = identifier
super().__init__(f"{resource} 未找到:{identifier}")
# 使用自定义异常
def validate_age(age):
if not isinstance(age, int):
raise ValidationError("age", "必须是整数")
if age < 0 or age > 150:
raise ValidationError("age", "必须在 0-150 之间")
return True
def find_user(user_id):
users = {1: "Alice", 2: "Bob"}
if user_id not in users:
raise NotFoundError("用户", user_id)
return users[user_id]
# 测试
try:
validate_age(-5)
except ValidationError as e:
print(e) # 验证失败 - age: 必须在 0-150 之间
print(f"字段:{e.field}")
print(f"消息:{e.message}")
try:
user = find_user(99)
except NotFoundError as e:
print(e) # 用户 未找到:99
# 异常链
try:
try:
result = 10 / 0
except ZeroDivisionError as e:
raise ValueError("计算错误") from e
except ValueError as e:
print(f"错误:{e}")
print(f"原因:{e.__cause__}")
9.4 异常最佳实践
# 1. 只捕获你能处理的异常
# 不好
try:
result = some_operation()
except Exception:
pass # 吞掉所有异常
# 好
try:
result = some_operation()
except SpecificError as e:
log_error(e)
result = default_value
# 2. 使用 finally 清理资源
# 不好
f = open("file.txt")
content = f.read()
f.close() # 如果上面出错,这行不会执行
# 好
f = None
try:
f = open("file.txt")
content = f.read()
finally:
if f:
f.close()
# 最好(使用 with)
with open("file.txt") as f:
content = f.read()
# 3. EAFP 原则(Easier to Ask Forgiveness than Permission)
# Python 风格:先做,出错再处理
# 不好(LBYL - Look Before You Leap)
if key in dictionary:
value = dictionary[key]
else:
value = default
# 好(EAFP)
try:
value = dictionary[key]
except KeyError:
value = default
# 4. 提供有用的错误信息
class ConfigError(Exception):
def __init__(self, key, expected_type, actual_value):
self.key = key
self.expected_type = expected_type
self.actual_value = actual_value
super().__init__(
f"配置错误:'{key}' 应为 {expected_type.__name__} 类型,"
f"实际值为 {actual_value!r}({type(actual_value).__name__})"
)
9.5 断言
# assert 语句
def calculate_average(numbers):
assert len(numbers) > 0, "列表不能为空"
return sum(numbers) / len(numbers)
# 正常使用
print(calculate_average([1, 2, 3, 4, 5])) # 3.0
# 触发断言
try:
calculate_average([])
except AssertionError as e:
print(f"断言失败:{e}")
# 断言用于调试,不应替代异常处理
# 在生产环境中可以用 -O 标志禁用断言
# python -O script.py
# 自定义断言消息
def set_age(age):
assert isinstance(age, int), f"age 必须是整数,收到 {type(age).__name__}"
assert 0 <= age <= 150, f"age 必须在 0-150 之间,收到 {age}"
return age
# 使用 unittest 进行测试
import unittest
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(1 + 1, 2)
def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
1 / 0
if __name__ == "__main__":
unittest.main()
第十章:常用标准库
10.1 datetime 模块
from datetime import datetime, date, time, timedelta
# 获取当前时间
now = datetime.now()
print(now) # 2024-01-15 14:30:45.123456
# 创建特定日期时间
dt = datetime(2024, 1, 15, 14, 30, 0)
print(dt)
# 日期时间属性
print(now.year) # 2024
print(now.month) # 1
print(now.day) # 15
print(now.hour) # 14
print(now.minute) # 30
print(now.second) # 45
print(now.weekday()) # 0(周一)
# 格式化
print(now.strftime("%Y-%m-%d %H:%M:%S")) # 2024-01-15 14:30:45
print(now.strftime("%Y年%m月%d日")) # 2024年01月15日
# 解析字符串
dt = datetime.strptime("2024-01-15", "%Y-%m-%d")
print(dt)
# 时间运算
tomorrow = now + timedelta(days=1)
last_week = now - timedelta(weeks=1)
print(tomorrow)
print(last_week)
# 两个日期之间的差
d1 = datetime(2024, 1, 1)
d2 = datetime(2024, 12, 31)
diff = d2 - d1
print(f"相差 {diff.days} 天")
# date 和 time 对象
today = date.today()
print(today)
current_time = datetime.now().time()
print(current_time)
10.2 re 模块(正则表达式)
import re
# 基本匹配
text = "我的电话是 13812345678,邮箱是 test@example.com"
# 查找手机号
phone_pattern = r"1[3-9]\d{9}"
phone = re.search(phone_pattern, text)
if phone:
print(f"手机号:{phone.group()}")
# 查找邮箱
email_pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
email = re.search(email_pattern, text)
if email:
print(f"邮箱:{email.group()}")
# 常用函数
text = "Hello 123 World 456 Python 789"
# findall - 查找所有匹配
numbers = re.findall(r"\d+", text)
print(numbers) # ['123', '456', '789']
# sub - 替换
result = re.sub(r"\d+", "NUM", text)
print(result) # Hello NUM World NUM Python NUM
# split - 分割
parts = re.split(r"\s+", text)
print(parts) # ['Hello', '123', 'World', '456', 'Python', '789']
# 分组
text = "2024-01-15"
pattern = r"(\d{4})-(\d{2})-(\d{2})"
match = re.match(pattern, text)
if match:
year, month, day = match.groups()
print(f"年:{year},月:{month},日:{day}")
# 命名分组
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
match = re.match(pattern, text)
if match:
print(match.group("year")) # 2024
# 编译正则(提高性能)
email_re = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
emails = email_re.findall("test@example.com and admin@site.org")
print(emails)
# 常用正则模式
# \d - 数字 [0-9]
# \D - 非数字
# \w - 字母数字下划线 [a-zA-Z0-9_]
# \W - 非字母数字下划线
# \s - 空白字符
# \S - 非空白字符
# . - 任意字符(除换行)
# * - 0 次或多次
# + - 1 次或多次
# ? - 0 次或 1 次
# {n} - 恰好 n 次
# {n,m} - n 到 m 次
# ^ - 开头
# $ - 结尾
# [] - 字符类
# | - 或
# () - 分组
10.3 collections 模块
from collections import Counter, defaultdict, OrderedDict, deque, namedtuple
# Counter - 计数器
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counter = Counter(words)
print(counter) # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(counter.most_common(2)) # [('apple', 3), ('banana', 2)]
# defaultdict - 默认字典
dd = defaultdict(list)
dd["fruits"].append("apple")
dd["fruits"].append("banana")
dd["vegetables"].append("carrot")
print(dict(dd))
# OrderedDict - 有序字典(Python 3.7+ 普通字典也有序)
od = OrderedDict()
od["b"] = 2
od["a"] = 1
od["c"] = 3
# deque - 双端队列
dq = deque([1, 2, 3])
dq.append(4) # 右端添加
dq.appendleft(0) # 左端添加
dq.pop() # 右端弹出
dq.popleft() # 左端弹出
dq.rotate(1) # 旋转
# 固定长度 deque
fixed_dq = deque(maxlen=3)
fixed_dq.append(1)
fixed_dq.append(2)
fixed_dq.append(3)
fixed_dq.append(4) # 自动丢弃最左边的元素
print(fixed_dq) # deque([2, 3, 4], maxlen=3)
# namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(10, 20)
print(p.x, p.y) # 10 20
10.4 itertools 模块
import itertools
# count - 无限计数器
counter = itertools.count(start=1, step=2)
print(next(counter)) # 1
print(next(counter)) # 3
print(next(counter)) # 5
# cycle - 无限循环
colors = itertools.cycle(["red", "green", "blue"])
print(next(colors)) # red
print(next(colors)) # green
print(next(colors)) # blue
print(next(colors)) # red
# chain - 连接迭代器
result = list(itertools.chain([1, 2, 3], [4, 5, 6]))
print(result) # [1, 2, 3, 4, 5, 6]
# product - 笛卡尔积
result = list(itertools.product("AB", "12"))
print(result) # [('A', '1'), ('A', '2'), ('B', '1'), ('B', '2')]
# permutations - 排列
result = list(itertools.permutations("ABC", 2))
print(result) # [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
# combinations - 组合
result = list(itertools.combinations("ABC", 2))
print(result) # [('A', 'B'), ('A', 'C'), ('B', 'C')]
# groupby - 分组
data = [("A", 1), ("A", 2), ("B", 3), ("B", 4), ("A", 5)]
data.sort(key=lambda x: x[0]) # 必须先排序
for key, group in itertools.groupby(data, key=lambda x: x[0]):
print(key, list(group))
# islice - 切片迭代器
result = list(itertools.islice(range(100), 5, 15, 2))
print(result) # [5, 7, 9, 11, 13]
10.5 functools 模块
import functools
# lru_cache - 缓存装饰器
@functools.lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(50)) # 12586269025
print(fibonacci.cache_info()) # 缓存信息
# partial - 偏函数
def power(base, exponent):
return base ** exponent
square = functools.partial(power, exponent=2)
cube = functools.partial(power, exponent=3)
print(square(5)) # 25
print(cube(3)) # 27
# reduce - 累积计算
numbers = [1, 2, 3, 4, 5]
total = functools.reduce(lambda x, y: x + y, numbers)
print(total) # 15
# total_ordering - 自动生成比较方法
@functools.total_ordering
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __eq__(self, other):
return self.grade == other.grade
def __lt__(self, other):
return self.grade < other.grade
s1 = Student("Alice", 88)
s2 = Student("Bob", 95)
print(s1 < s2) # True
print(s1 >= s2) # False(自动生成)
# wraps - 保留被装饰函数的信息
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""示例函数"""
pass
print(example.__name__) # example
print(example.__doc__) # 示例函数
10.6 hashlib 模块
import hashlib
# MD5
data = "Hello, World!".encode("utf-8")
md5 = hashlib.md5(data).hexdigest()
print(f"MD5: {md5}")
# SHA-1
sha1 = hashlib.sha1(data).hexdigest()
print(f"SHA-1: {sha1}")
# SHA-256
sha256 = hashlib.sha256(data).hexdigest()
print(f"SHA-256: {sha256}")
# 分块计算(大文件)
def file_hash(filename, algorithm="sha256"):
h = hashlib.new(algorithm)
with open(filename, "rb") as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
# 密码加盐哈希
import os
import hashlib
def hash_password(password):
salt = os.urandom(32)
key = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100000)
return salt + key
def verify_password(stored, password):
salt = stored[:32]
stored_key = stored[32:]
new_key = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100000)
return new_key == stored_key
10.7 random 模块
import random
# 随机整数
print(random.randint(1, 100)) # 1-100 之间的随机整数
print(random.randrange(0, 10, 2)) # 0-9 之间的随机偶数
# 随机浮点数
print(random.random()) # 0-1 之间的随机浮点数
print(random.uniform(1.0, 10.0)) # 1-10 之间的随机浮点数
# 随机选择
colors = ["red", "green", "blue", "yellow"]
print(random.choice(colors)) # 随机选择一个
print(random.choices(colors, k=3)) # 随机选择 3 个(可重复)
print(random.sample(colors, 2)) # 随机选择 2 个(不重复)
# 打乱顺序
deck = list(range(1, 53))
random.shuffle(deck)
print(deck[:5]) # 前 5 张牌
# 设置种子(可复现)
random.seed(42)
print(random.randint(1, 100)) # 每次运行都一样
# 加密安全的随机数
import secrets
token = secrets.token_hex(16)
print(token)
secure_number = secrets.randbelow(100)
print(secure_number)
第十一章:实战项目
11.1 项目一:通讯录管理系统
"""
通讯录管理系统
功能:添加、删除、查找、显示、保存到文件
"""
import json
from pathlib import Path
class ContactManager:
def __init__(self, filename="contacts.json"):
self.filename = Path(filename)
self.contacts = self.load_contacts()
def load_contacts(self):
"""从文件加载联系人"""
if self.filename.exists():
with open(self.filename, "r", encoding="utf-8") as f:
return json.load(f)
return {}
def save_contacts(self):
"""保存联系人到文件"""
with open(self.filename, "w", encoding="utf-8") as f:
json.dump(self.contacts, f, ensure_ascii=False, indent=2)
def add_contact(self, name, phone, email=""):
"""添加联系人"""
if name in self.contacts:
print(f"联系人 {name} 已存在")
return False
self.contacts[name] = {
"phone": phone,
"email": email
}
self.save_contacts()
print(f"成功添加联系人:{name}")
return True
def delete_contact(self, name):
"""删除联系人"""
if name not in self.contacts:
print(f"联系人 {name} 不存在")
return False
del self.contacts[name]
self.save_contacts()
print(f"成功删除联系人:{name}")
return True
def search_contact(self, keyword):
"""查找联系人"""
results = {}
for name, info in self.contacts.items():
if keyword.lower() in name.lower() or keyword in info.get("phone", ""):
results[name] = info
if results:
print(f"找到 {len(results)} 个匹配的联系人:")
for name, info in results.items():
print(f" 姓名:{name}")
print(f" 电话:{info['phone']}")
print(f" 邮箱:{info.get('email', '未设置')}")
print("-" * 30)
else:
print("未找到匹配的联系人")
return results
def list_contacts(self):
"""显示所有联系人"""
if not self.contacts:
print("通讯录为空")
return
print(f"\n共有 {len(self.contacts)} 个联系人:")
print("=" * 40)
for name, info in sorted(self.contacts.items()):
print(f"姓名:{name}")
print(f"电话:{info['phone']}")
print(f"邮箱:{info.get('email', '未设置')}")
print("-" * 40)
def update_contact(self, name, phone=None, email=None):
"""更新联系人信息"""
if name not in self.contacts:
print(f"联系人 {name} 不存在")
return False
if phone:
self.contacts[name]["phone"] = phone
if email:
self.contacts[name]["email"] = email
self.save_contacts()
print(f"成功更新联系人:{name}")
return True
def main():
manager = ContactManager()
while True:
print("\n===== 通讯录管理系统 =====")
print("1. 添加联系人")
print("2. 删除联系人")
print("3. 查找联系人")
print("4. 显示所有联系人")
print("5. 更新联系人")
print("0. 退出系统")
print("=" * 25)
choice = input("\n请选择操作:").strip()
if choice == "1":
name = input("请输入姓名:").strip()
phone = input("请输入电话:").strip()
email = input("请输入邮箱(可选):").strip()
manager.add_contact(name, phone, email)
elif choice == "2":
name = input("请输入要删除的联系人姓名:").strip()
confirm = input(f"确认删除 {name}?(y/n):").strip()
if confirm.lower() == "y":
manager.delete_contact(name)
elif choice == "3":
keyword = input("请输入查找关键词:").strip()
manager.search_contact(keyword)
elif choice == "4":
manager.list_contacts()
elif choice == "5":
name = input("请输入要更新的联系人姓名:").strip()
phone = input("请输入新电话(留空不修改):").strip()
email = input("请输入新邮箱(留空不修改):").strip()
manager.update_contact(name, phone or None, email or None)
elif choice == "0":
print("感谢使用,再见!")
break
else:
print("无效选择,请重试")
if __name__ == "__main__":
main()
11.2 项目二:学生成绩管理系统
"""
学生成绩管理系统
功能:录入成绩、计算统计、排名、导出报表
"""
import csv
from dataclasses import dataclass
from typing import List, Dict
from statistics import mean, median, stdev
@dataclass
class Student:
student_id: str
name: str
scores: Dict[str, float]
@property
def average(self):
if not self.scores:
return 0
return mean(self.scores.values())
@property
def total(self):
return sum(self.scores.values())
class GradeManager:
def __init__(self):
self.students: List[Student] = []
self.subjects: List[str] = []
def set_subjects(self, subjects: List[str]):
"""设置科目"""
self.subjects = subjects
print(f"已设置科目:{', '.join(subjects)}")
def add_student(self, student_id: str, name: str):
"""添加学生"""
# 检查学号是否重复
if any(s.student_id == student_id for s in self.students):
print(f"学号 {student_id} 已存在")
return False
student = Student(student_id=student_id, name=name, scores={})
self.students.append(student)
print(f"成功添加学生:{name}")
return student
def input_scores(self, student_id: str, scores: Dict[str, float]):
"""录入成绩"""
student = self.find_student(student_id)
if not student:
print(f"未找到学号为 {student_id} 的学生")
return False
for subject, score in scores.items():
if subject not in self.subjects:
print(f"未知科目:{subject}")
continue
if not 0 <= score <= 100:
print(f"成绩必须在 0-100 之间:{subject}={score}")
continue
student.scores[subject] = score
print(f"已为 {student.name} 录入成绩")
return True
def find_student(self, student_id: str):
"""查找学生"""
for student in self.students:
if student.student_id == student_id:
return student
return None
def get_statistics(self, subject: str = None):
"""获取统计数据"""
if subject:
scores = [s.scores.get(subject, 0) for s in self.students if subject in s.scores]
if not scores:
return None
return {
"科目": subject,
"人数": len(scores),
"平均分": round(mean(scores), 2),
"最高分": max(scores),
"最低分": min(scores),
"中位数": round(median(scores), 2),
"标准差": round(stdev(scores), 2) if len(scores) > 1 else 0
}
else:
stats = {}
for sub in self.subjects:
sub_stats = self.get_statistics(sub)
if sub_stats:
stats[sub] = sub_stats
return stats
def get_ranking(self):
"""获取排名"""
ranked = sorted(self.students, key=lambda s: s.total, reverse=True)
ranking = []
for i, student in enumerate(ranked, 1):
ranking.append({
"排名": i,
"学号": student.student_id,
"姓名": student.name,
"总分": student.total,
"平均分": round(student.average, 2)
})
return ranking
def export_csv(self, filename: str):
"""导出成绩到 CSV"""
with open(filename, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
# 写入表头
header = ["学号", "姓名"] + self.subjects + ["总分", "平均分"]
writer.writerow(header)
# 写入数据
for student in self.students:
row = [student.student_id, student.name]
for subject in self.subjects:
row.append(student.scores.get(subject, ""))
row.extend([student.total, round(student.average, 2)])
writer.writerow(row)
print(f"成绩已导出到 {filename}")
def main():
manager = GradeManager()
# 设置科目
manager.set_subjects(["语文", "数学", "英语"])
# 添加学生并录入成绩
students_data = [
("001", "张三", {"语文": 85, "数学": 92, "英语": 78}),
("002", "李四", {"语文": 90, "数学": 88, "英语": 95}),
("003", "王五", {"语文": 76, "数学": 95, "英语": 82}),
("004", "赵六", {"语文": 88, "数学": 76, "英语": 90}),
("005", "钱七", {"语文": 92, "数学": 85, "英语": 88}),
]
for sid, name, scores in students_data:
student = manager.add_student(sid, name)
if student:
manager.input_scores(sid, scores)
# 显示统计信息
print("\n===== 成绩统计 =====")
stats = manager.get_statistics()
for subject, stat in stats.items():
print(f"\n{subject}:")
for key, value in stat.items():
print(f" {key}: {value}")
# 显示排名
print("\n===== 成绩排名 =====")
ranking = manager.get_ranking()
for r in ranking:
print(f"第{r['排名']}名:{r['姓名']} "
f"(总分:{r['总分']},平均分:{r['平均分']})")
# 导出报表
manager.export_csv("grades.csv")
if __name__ == "__main__":
main()
11.3 项目三:Web 爬虫基础
"""
简单的网页爬虫
使用 requests 和 BeautifulSoup
注意:需要安装第三方库
pip install requests beautifulsoup4
"""
import requests
from bs4 import BeautifulSoup
import json
import time
from pathlib import Path
class SimpleCrawler:
def __init__(self, delay=1):
self.delay = delay
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/91.0.4472.124 Safari/537.36"
})
def fetch(self, url: str) -> str:
"""获取网页内容"""
try:
response = self.session.get(url, timeout=10)
response.raise_for_status()
response.encoding = response.apparent_encoding
return response.text
except requests.RequestException as e:
print(f"请求失败:{e}")
return ""
def parse_html(self, html: str) -> BeautifulSoup:
"""解析 HTML"""
return BeautifulSoup(html, "html.parser")
def extract_links(self, soup: BeautifulSoup, base_url: str = ""):
"""提取所有链接"""
links = []
for a_tag in soup.find_all("a", href=True):
href = a_tag["href"]
if href.startswith("http"):
links.append(href)
elif href.startswith("/") and base_url:
links.append(base_url + href)
return links
def extract_text(self, soup: BeautifulSoup):
"""提取正文文本"""
# 移除 script 和 style 标签
for tag in soup(["script", "style"]):
tag.decompose()
text = soup.get_text(separator="\n", strip=True)
return text
def extract_images(self, soup: BeautifulSoup, base_url: str = ""):
"""提取所有图片"""
images = []
for img in soup.find_all("img", src=True):
src = img["src"]
alt = img.get("alt", "")
if src.startswith("http"):
images.append({"src": src, "alt": alt})
elif src.startswith("/") and base_url:
images.append({"src": base_url + src, "alt": alt})
return images
def save_to_file(self, content: str, filename: str):
"""保存内容到文件"""
path = Path(filename)
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
print(f"已保存到 {filename}")
def crawl(self, url: str):
"""爬取单个页面"""
print(f"正在爬取:{url}")
html = self.fetch(url)
if not html:
return None
soup = self.parse_html(html)
result = {
"url": url,
"title": soup.title.string if soup.title else "",
"text": self.extract_text(soup)[:1000], # 只取前1000字
"links": self.extract_links(soup),
"images": self.extract_images(soup)
}
time.sleep(self.delay)
return result
# 使用示例
def demo():
crawler = SimpleCrawler(delay=1)
# 爬取单个页面
url = "https://example.com"
result = crawler.crawl(url)
if result:
print(f"标题:{result['title']}")
print(f"链接数:{len(result['links'])}")
print(f"图片数:{len(result['images'])}")
# 保存结果
crawler.save_to_file(
json.dumps(result, ensure_ascii=False, indent=2),
"crawl_result.json"
)
if __name__ == "__main__":
demo()
11.4 项目四:文件批量重命名工具
"""
文件批量重命名工具
功能:按规则批量重命名文件
"""
import os
from pathlib import Path
from datetime import datetime
import re
class FileRenamer:
def __init__(self, directory: str):
self.directory = Path(directory)
if not self.directory.is_dir():
raise ValueError(f"目录不存在:{directory}")
def list_files(self, pattern: str = "*"):
"""列出匹配的文件"""
files = list(self.directory.glob(pattern))
files = [f for f in files if f.is_file()]
return sorted(files)
def preview_rename(self, files, rename_func):
"""预览重命名结果"""
print("\n预览重命名:")
print("-" * 60)
for file in files:
new_name = rename_func(file)
if new_name != file.name:
print(f" {file.name} → {new_name}")
else:
print(f" {file.name} (不修改)")
print("-" * 60)
def execute_rename(self, files, rename_func, dry_run=True):
"""执行重命名"""
renamed = 0
for file in files:
new_name = rename_func(file)
new_path = file.parent / new_name
if new_name == file.name:
continue
if dry_run:
print(f"[模拟] {file.name} → {new_name}")
else:
if new_path.exists():
print(f"[跳过] {new_name} 已存在")
continue
file.rename(new_path)
print(f"[完成] {file.name} → {new_name}")
renamed += 1
print(f"\n共{'模拟' if dry_run else ''}重命名 {renamed} 个文件")
# 重命名策略
@staticmethod
def add_prefix(prefix):
"""添加前缀"""
return lambda file: f"{prefix}{file.name}"
@staticmethod
def add_suffix(suffix):
"""添加后缀"""
return lambda file: f"{file.stem}{suffix}{file.suffix}"
@staticmethod
def add_number(start=1, step=1, digits=3):
"""添加序号"""
counter = [start]
def rename(file):
name = f"{counter[0]:0{digits}d}_{file.name}"
counter[0] += step
return name
return rename
@staticmethod
def change_extension(new_ext):
"""修改扩展名"""
if not new_ext.startswith("."):
new_ext = "." + new_ext
return lambda file: file.stem + new_ext
@staticmethod
def replace_text(old_text, new_text):
"""替换文本"""
return lambda file: file.name.replace(old_text, new_text)
@staticmethod
def regex_replace(pattern, replacement):
"""正则替换"""
compiled = re.compile(pattern)
return lambda file: compiled.sub(replacement, file.name)
@staticmethod
def to_lower():
"""转换为小写"""
return lambda file: file.name.lower()
@staticmethod
def to_upper():
"""转换为大写"""
return lambda file: file.name.upper()
@staticmethod
def add_date(fmt="%Y%m%d"):
"""添加日期"""
date_str = datetime.now().strftime(fmt)
return lambda file: f"{date_str}_{file.name}"
def main():
# 示例:重命名当前目录下的文件
directory = input("请输入目录路径(默认当前目录):").strip() or "."
try:
renamer = FileRenamer(directory)
except ValueError as e:
print(e)
return
files = renamer.list_files()
if not files:
print("目录中没有文件")
return
print(f"\n找到 {len(files)} 个文件:")
for f in files[:10]:
print(f" {f.name}")
if len(files) > 10:
print(f" ... 还有 {len(files) - 10} 个文件")
print("\n请选择重命名策略:")
print("1. 添加前缀")
print("2. 添加后缀")
print("3. 添加序号")
print("4. 替换文本")
print("5. 转换为小写")
print("6. 转换为大写")
print("7. 添加日期前缀")
choice = input("\n请选择:").strip()
rename_func = None
if choice == "1":
prefix = input("请输入前缀:")
rename_func = FileRenamer.add_prefix(prefix)
elif choice == "2":
suffix = input("请输入后缀:")
rename_func = FileRenamer.add_suffix(suffix)
elif choice == "3":
start = int(input("起始序号(默认1):") or "1")
digits = int(input("序号位数(默认3):") or "3")
rename_func = FileRenamer.add_number(start=start, digits=digits)
elif choice == "4":
old = input("要替换的文本:")
new = input("替换为:")
rename_func = FileRenamer.replace_text(old, new)
elif choice == "5":
rename_func = FileRenamer.to_lower()
elif choice == "6":
rename_func = FileRenamer.to_upper()
elif choice == "7":
rename_func = FileRenamer.add_date()
else:
print("无效选择")
return
# 预览
renamer.preview_rename(files, rename_func)
# 确认执行
confirm = input("\n确认执行?(y/n/模拟):").strip().lower()
if confirm == "y":
renamer.execute_rename(files, rename_func, dry_run=False)
elif confirm == "模拟":
renamer.execute_rename(files, rename_func, dry_run=True)
else:
print("已取消")
if __name__ == "__main__":
main()
11.5 项目五:简单的 REST API
"""
使用 Flask 创建简单的 REST API
注意:需要安装 Flask
pip install flask
"""
from flask import Flask, jsonify, request
from datetime import datetime
app = Flask(__name__)
# 模拟数据库
todos = [
{"id": 1, "title": "学习 Python", "completed": True, "created_at": "2024-01-15"},
{"id": 2, "title": "完成项目", "completed": False, "created_at": "2024-01-16"},
]
next_id = 3
@app.route("/")
def index():
return jsonify({
"message": "Todo API",
"endpoints": {
"GET /todos": "获取所有待办事项",
"GET /todos/<id>": "获取单个待办事项",
"POST /todos": "创建待办事项",
"PUT /todos/<id>": "更新待办事项",
"DELETE /todos/<id>": "删除待办事项"
}
})
@app.route("/todos", methods=["GET"])
def get_todos():
# 查询参数:completed=true/false
completed = request.args.get("completed")
if completed is not None:
completed = completed.lower() == "true"
filtered = [t for t in todos if t["completed"] == completed]
return jsonify(filtered)
return jsonify(todos)
@app.route("/todos/<int:todo_id>", methods=["GET"])
def get_todo(todo_id):
todo = next((t for t in todos if t["id"] == todo_id), None)
if todo is None:
return jsonify({"error": "待办事项不存在"}), 404
return jsonify(todo)
@app.route("/todos", methods=["POST"])
def create_todo():
global next_id
if not request.json or "title" not in request.json:
return jsonify({"error": "缺少 title 字段"}), 400
todo = {
"id": next_id,
"title": request.json["title"],
"completed": request.json.get("completed", False),
"created_at": datetime.now().strftime("%Y-%m-%d")
}
todos.append(todo)
next_id += 1
return jsonify(todo), 201
@app.route("/todos/<int:todo_id>", methods=["PUT"])
def update_todo(todo_id):
todo = next((t for t in todos if t["id"] == todo_id), None)
if todo is None:
return jsonify({"error": "待办事项不存在"}), 404
if not request.json:
return jsonify({"error": "请求体不能为空"}), 400
if "title" in request.json:
todo["title"] = request.json["title"]
if "completed" in request.json:
todo["completed"] = request.json["completed"]
return jsonify(todo)
@app.route("/todos/<int:todo_id>", methods=["DELETE"])
def delete_todo(todo_id):
global todos
todo = next((t for t in todos if t["id"] == todo_id), None)
if todo is None:
return jsonify({"error": "待办事项不存在"}), 404
todos = [t for t in todos if t["id"] != todo_id]
return jsonify({"message": "删除成功"})
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "资源不存在"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"error": "服务器内部错误"}), 500
if __name__ == "__main__":
app.run(debug=True, port=5000)
# 测试命令:
# curl http://localhost:5000/todos
# curl http://localhost:5000/todos/1
# curl -X POST -H "Content-Type: application/json" -d '{"title": "新任务"}' http://localhost:5000/todos
# curl -X PUT -H "Content-Type: application/json" -d '{"completed": true}' http://localhost:5000/todos/1
# curl -X DELETE http://localhost:5000/todos/1
第十二章:常见问题与总结
12.1 常见错误与解决
# 1. IndentationError - 缩进错误
# Python 使用缩进表示代码块,必须保持一致
# 错误示例:
# def func():
# print("hello")
# print("world") # 缩进不一致
# 2. NameError - 名称未定义
# 变量在使用前必须先定义
# 错误示例:
# print(x) # x 未定义
# 3. TypeError - 类型错误
# 错误示例:
# result = "hello" + 42 # 字符串不能与整数相加
# 正确做法:
# result = "hello" + str(42)
# 4. IndexError - 索引越界
# 错误示例:
# lst = [1, 2, 3]
# print(lst[10]) # 索引超出范围
# 5. KeyError - 字典键不存在
# 错误示例:
# d = {"a": 1}
# print(d["b"]) # 键不存在
# 正确做法:
# print(d.get("b", "default"))
# 6. AttributeError - 属性不存在
# 错误示例:
# s = "hello"
# s.append("x") # 字符串没有 append 方法
# 7. ValueError - 值错误
# 错误示例:
# num = int("abc") # 无法转换为整数
# 8. ImportError - 导入错误
# 错误示例:
# import nonexistent_module
# 9. FileNotFoundError - 文件不存在
# 错误示例:
# f = open("nonexistent.txt")
# 10. ZeroDivisionError - 除以零
# 错误示例:
# result = 10 / 0
12.2 Python 编码最佳实践
# 1. 使用有意义的变量名
# 不好
# x = 5
# 好
# max_retries = 5
# 2. 遵循 PEP 8 编码规范
# - 使用 4 个空格缩进
# - 行长度不超过 79 个字符
# - 函数之间空两行
# - 类之间空两行
# - 使用 snake_case 命名变量和函数
# - 使用 CamelCase 命名类
# - 使用 UPPER_CASE 命名常量
# 3. 使用列表推导式(适当情况下)
# 不好
# squares = []
# for x in range(10):
# squares.append(x ** 2)
# 好
# squares = [x ** 2 for x in range(10)]
# 4. 使用 with 语句管理资源
# 不好
# f = open("file.txt")
# content = f.read()
# f.close()
# 好
# with open("file.txt") as f:
# content = f.read()
# 5. 使用 f-string 进行字符串格式化
# 不好
# msg = "Hello, %s! You are %d years old." % (name, age)
# 好
# msg = f"Hello, {name}! You are {age} years old."
# 6. 使用 enumerate() 而不是 range(len())
# 不好
# for i in range(len(items)):
# print(i, items[i])
# 好
# for i, item in enumerate(items):
# print(i, item)
# 7. 使用 zip() 并行遍历
# 不好
# for i in range(len(names)):
# print(names[i], ages[i])
# 好
# for name, age in zip(names, ages):
# print(name, age)
# 8. 使用 get() 访问字典
# 不好
# if key in d:
# value = d[key]
# 好
# value = d.get(key, default_value)
# 9. 使用异常处理而不是条件检查(EAFP)
# 不好
# if hasattr(obj, "attr"):
# value = obj.attr
# 好
# try:
# value = obj.attr
# except AttributeError:
# value = default
# 10. 编写文档字符串
def calculate_bmi(weight, height):
"""
计算 BMI(身体质量指数)。
参数:
weight (float): 体重,单位:千克
height (float): 身高,单位:米
返回:
float: BMI 值
示例:
>>> calculate_bmi(70, 1.75)
22.86
"""
return round(weight / (height ** 2), 2)
12.3 学习资源推荐
- 官方文档:https://docs.python.org/3/
- Python 教程:https://docs.python.org/3/tutorial/
- PEP 8 编码规范:https://peps.python.org/pep-0008/
- Real Python:https://realpython.com/
- 廖雪峰 Python 教程:https://liaoxuefeng.com/
- LeetCode:https://leetcode.com/(练习算法)
- GitHub:https://github.com/(阅读开源项目)
12.4 下一步学习建议
- Web 开发:学习 Flask 或 Django 框架
- 数据分析:学习 NumPy、Pandas、Matplotlib
- 机器学习:学习 Scikit-learn、TensorFlow 或 PyTorch
- 自动化:学习 Selenium、Scrapy
- DevOps:学习 Docker、Kubernetes、CI/CD
- 游戏开发:学习 Pygame
- 桌面应用:学习 Tkinter 或 PyQt
12.5 总结
恭喜你完成了 Python 入门教程的学习!让我们回顾一下本教程涵盖的内容:
- 环境搭建:安装 Python、选择 IDE、编写第一个程序
- 基础语法:变量、数据类型、运算符、表达式
- 控制流:条件语句、循环语句、循环控制
- 函数:定义、参数、返回值、作用域、装饰器
- 数据结构:列表、元组、字典、集合
- 面向对象:类、继承、多态、魔术方法
- 文件操作:文本文件、CSV、JSON、pathlib
- 异常处理:try-except、自定义异常、最佳实践
- 标准库:datetime、re、collections、itertools
- 实战项目:通讯录、成绩管理、爬虫、文件重命名、REST API
Python 是一门功能强大且易于学习的编程语言。掌握了这些基础知识后,你已经具备了进一步深入学习的能力。记住,编程是一项实践性很强的技能,多写代码、多做项目是提高编程能力的最佳途径。
祝你在 Python 的世界里编程愉快!🚀
本教程内容仅供参考学习,建议结合实际项目练习加深理解。