Python基础语法教程

🐍 Python 基础语法这一篇就够了

适用版本 : Python 3.8+

目标读者 : 初学者及有一定基础想要系统复习的开发者

最后更新: 2026-07-21


目录

  1. [Python 简介](#Python 简介)
  2. 环境搭建与第一个程序
  3. 变量与数据类型
  4. 运算符
  5. 字符串详解
  6. 列表(List)
  7. 元组(Tuple)
  8. 字典(Dictionary)
  9. 集合(Set)
  10. 条件判断
  11. 循环语句
  12. 函数
  13. [Lambda 表达式](#Lambda 表达式)
  14. 列表推导式与生成器表达式
  15. 迭代器与生成器
  16. 装饰器
  17. 模块与包
  18. 文件操作
  19. 异常处理
  20. 面向对象编程
  21. 常用内置函数
  22. 类型注解
  23. 虚拟环境与包管理
  24. [编码规范(PEP 8)](#编码规范(PEP 8))
  25. 综合练习

1. Python 简介

1.1 什么是 Python?

Python 是一种高级、解释型、面向对象 的编程语言,由 Guido van Rossum 于 1991 年发布。它以简洁易读 的语法和强大的标准库而闻名。

1.2 Python 的特点

特点 说明
简洁易读 使用缩进表示代码块,语法接近自然语言
解释型语言 无需编译,逐行解释执行
动态类型 变量无需声明类型,运行时自动推断
跨平台 支持 Windows、macOS、Linux 等
丰富的库 拥有庞大的标准库和第三方生态
多范式 支持面向对象、过程式、函数式编程

1.3 Python 的应用领域

  • Web 开发: Django、Flask、FastAPI
  • 数据科学与人工智能: NumPy、Pandas、PyTorch、TensorFlow
  • 自动化运维: Ansible、SaltStack
  • 网络爬虫: Scrapy、BeautifulSoup、Selenium
  • 桌面应用: PyQt、Tkinter
  • 游戏开发: Pygame

2. 环境搭建与第一个程序

2.1 安装 Python

python.org 下载安装包。安装时务必勾选 "Add Python to PATH"

验证安装:

bash 复制代码
python --version
# 输出示例: Python 3.11.5

2.2 第一个程序:Hello, World!

💡 代码说明:创建第一个 Python 程序,用 print() 函数在屏幕上输出文字:

python 复制代码
# hello.py
print("Hello, World!")

🔑 要点总结:运行后屏幕上将显示 Hello, World! ------你已经成功编写了第一个 Python 程序。

运行:

bash 复制代码
python hello.py
# 输出: Hello, World!

2.3 交互式解释器(REPL)

在终端输入 python 即可进入交互模式:

💡 代码说明:在终端输入 python 进入交互模式,可以逐行输入代码并立即看到结果,适合快速测试:

python 复制代码
>>> 1 + 2
3
>>> print("你好,世界!")
你好,世界!
>>> exit()  # 退出交互模式

🔑 要点总结:REPL 是学习 Python 的利器------每学一个新语法都可以先在交互环境中试一试。输入 exit() 即可退出。

2.4 IDE 推荐

IDE 特点
VS Code 免费、轻量、插件丰富(推荐)
PyCharm 专业的 Python IDE,功能强大
Jupyter Notebook 适合数据分析和科学计算
IDLE Python 自带,入门够用

3. 变量与数据类型

3.1 变量命名规则

💡 代码说明:Python 变量名由字母、数字和下划线组成,但不能以数字开头,也不能使用关键字。来看几个例子:

python 复制代码
# ✅ 合法的变量名
name = "Alice"
age = 25
first_name = "Bob"
_name = "hidden"
user123 = "valid"

# ❌ 非法的变量名
# 123abc = "error"     # 不能以数字开头
# my-name = "error"     # 不能包含连字符
# class = "error"       # 不能使用关键字

🔑 要点总结:记住三条核心规则:不能数字开头、不能用关键字、区分大小写。按照 snake_case 风格命名是最佳实践。

命名规范(PEP 8):

  • 变量和函数:小写 + 下划线 snake_case
  • 类名:大驼峰 PascalCase
  • 常量:全大写 UPPER_CASE

3.2 Python 关键字

💡 代码说明:Python 保留了一些有特殊含义的单词(关键字),不能用作变量名。通过 keyword 模块可以查看完整列表:

python 复制代码
import keyword
print(keyword.kwlist)
# ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await',
#  'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except',
#  'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
#  'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return',
#  'try', 'while', 'with', 'yield']

🔑 要点总结:总共 35 个关键字(Python 3.8+),像 if、for、class、def 等都在其中,取名时注意避开。

3.3 基本数据类型

💡 代码说明:Python 有六大基本数据类型:整数(int)、浮点数(float)、复数(complex)、布尔值(bool)、字符串(str)和空值(None)。下面的代码展示了每种类型的用法:

python 复制代码
# --- 数值型 ---
# int(整数):无大小限制
a = 10
b = -5
big = 999999999999999999999999999999

# float(浮点数):双精度,约 15-17 位有效数字
pi = 3.14159
sci = 1.5e-3    # 科学计数法 = 0.0015

# complex(复数)
c = 3 + 4j
print(c.real)   # 3.0
print(c.imag)   # 4.0

# --- 布尔型 ---
# bool:True 或 False(首字母大写)
is_active = True
is_empty = False
# bool 是 int 的子类:True == 1, False == 0
print(True + True)   # 2
print(isinstance(True, int))  # True

# --- 字符串 ---
# str:使用单引号或双引号
name = 'Alice'
message = "Hello"
multiline = """第一行
第二行
第三行"""

# --- None 类型 ---
# None:表示"没有值"或"空"
result = None

🔑 要点总结:Python 的整数没有大小限制,布尔值是 int 的子类(True==1),None 表示无值。这些都是日常编程最基础的数据类型。

3.4 类型检查与转换

💡 代码说明:使用 type() 查看变量类型,isinstance() 检查继承关系,以及用 int()、float()、str() 等函数在不同类型间转换:

python 复制代码
# type() 查看类型
x = 42
print(type(x))          # <class 'int'>

# isinstance() 检查类型
print(isinstance(x, int))    # True
print(isinstance(x, (int, float)))  # True(多种类型之一)

# 类型转换(显式转换)
num_str = "123"
num = int(num_str)       # 字符串 -> 整数: 123
f = float("3.14")        # 字符串 -> 浮点数: 3.14
s = str(100)             # 整数 -> 字符串: "100"
b = bool(1)              # -> True (非0为True,0/空/None为False)
lst = list("hello")      # 字符串 -> 列表: ['h', 'e', 'l', 'l', 'o']

🔑 要点总结:isinstance() 比 type() 更灵活(支持子类判断),显式类型转换时要确保数据格式正确,否则会抛出 ValueError。

3.5 动态类型

💡 代码说明:Python 是动态类型语言,同一个变量可以在不同时刻指向不同类型的值,无需提前声明:

python 复制代码
# Python 是动态类型语言,变量可以随时改变类型
x = 10
print(type(x))  # <class 'int'>

x = "hello"
print(type(x))  # <class 'str'>

x = [1, 2, 3]
print(type(x))  # <class 'list'>

🔑 要点总结:动态类型让代码更灵活简洁,但也意味着要小心类型错误------这也是类型注解(第22章)存在的意义。

3.6 可变与不可变类型

💡 代码说明:理解可变(list/dict/set)和不可变(int/str/tuple)类型的区别,是掌握 Python 内存行为的关键:

python 复制代码
# 不可变类型:int, float, complex, str, tuple, frozenset, bool
a = "hello"
# a[0] = "H"     # ❌ TypeError: 'str' object does not support item assignment

# 可变类型:list, dict, set, bytearray
b = [1, 2, 3]
b[0] = 10         # ✅ 可以修改
print(b)          # [10, 2, 3]

🔑 要点总结:不可变对象无法原地修改,任何修改操作实际是创建新对象;可变对象则可以直接改变其内容。这影响了赋值、传参和拷贝的行为。


4. 运算符

4.1 算术运算符

💡 代码说明:Python 支持加减乘除、整除、取余、幂运算等基本算术运算,以及 abs()、round()、divmod() 等内置数学函数:

python 复制代码
a, b = 10, 3

print(a + b)    # 13  加
print(a - b)    # 7   减
print(a * b)    # 30  乘
print(a / b)    # 3.3333333333333335  除(总是返回float)
print(a // b)   # 3   整除(向下取整)
print(a % b)    # 1   取余
print(a ** b)   # 1000  幂运算
print(abs(-5))  # 5   绝对值
print(round(3.14159, 2))  # 3.14  四舍五入
print(pow(2, 3))            # 8   等价于 2**3

# divmod:同时返回商和余数
quotient, remainder = divmod(10, 3)
print(quotient, remainder)  # 3 1

🔑 要点总结:注意 / 总是返回 float,// 是向下取整。divmod() 同时返回商和余数,非常实用。

4.2 比较运算符

💡 代码说明:比较运算符用于比较两个值的大小关系,返回布尔值。Python 还支持链式比较和 is / == 两种相等性检查:

python 复制代码
a, b = 10, 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   小于等于

# 链式比较(Python 特有)
x = 5
print(0 < x < 10)   # True(等价于 0 < x and x < 10)

# is 和 == 的区别
# == 比较值是否相等
# is 比较是否为同一个对象(内存地址相同)
list_a = [1, 2, 3]
list_b = [1, 2, 3]
print(list_a == list_b)    # True  值相等
print(list_a is list_b)    # False 不同对象

# 对于小整数(-5 ~ 256)和短字符串,Python 使用缓存
a = 256; b = 256
print(a is b)   # True(同一缓存对象)
a = 257; b = 257
print(a is b)   # 可能 True 也可能 False(取决于实现)

🔑 要点总结:== 比较值,is 比较身份(内存地址)。对于小整数(-5~256),Python 使用缓存机制,所以 is 也可能返回 True------这是常见的面试考点。

4.3 逻辑运算符

💡 代码说明:Python 使用 and、or、not(而不是 &&、||、!)。逻辑运算支持短路求值,且可以作用于非布尔值:

python 复制代码
# and, or, not(注意:不是 &&, ||, !)
x, y = True, False

print(x and y)  # False  且
print(x or y)   # True   或
print(not x)    # False  非

# 短路求值
def get_true():
    print("get_true 被调用")
    return True

def get_false():
    print("get_false 被调用")
    return False

# and:第一个 False 后面的不会执行
print(get_false() and get_true())  # 只输出 get_false 被调用,返回 False

# or:第一个 True 后面的不会执行
print(get_true() or get_false())   # 只输出 get_true 被调用,返回 True

# 非布尔值的逻辑运算:返回第一个能决定结果的值
print(0 and 10)     # 0   (0 是 "假值",直接返回)
print([] and 10)    # []  (空列表是 "假值")
print(0 or 10)      # 10  (0 是假值,返回 10)
print("" or "default")  # "default"
print("hello" or "world")  # "hello" (第一个是真值)

# 常见的默认值模式
name = "" or "匿名用户"
print(name)  # 匿名用户

🔑 要点总结:短路求值是性能优化的好帮手。非布尔值的逻辑运算返回第一个能决定结果的值,常用于设置默认值(如 name = input_name or '匿名')。

4.4 位运算符

💡 代码说明:位运算符直接操作二进制位,包括按位与(&)、或(|)、异或(^)、取反(~)、左移(<<)、右移(>>):

python 复制代码
a, b = 5, 3  # 二进制: 0101, 0011

print(a & b)    # 1  (0101 & 0011 = 0001)  按位与
print(a | b)    # 7  (0101 | 0011 = 0111)  按位或
print(a ^ b)    # 6  (0101 ^ 0011 = 0110)  按位异或
print(~a)       # -6                       按位取反
print(a << 1)   # 10 (0101 << 1 = 1010)   左移
print(a >> 1)   # 2  (0101 >> 1 = 0010)   右移

🔑 要点总结:位运算在底层操作、权限标志、性能优化等场景很有用,但可读性较差,使用时建议加注释说明意图。

4.5 赋值运算符

💡 代码说明:除了基本赋值 =,Python 还支持复合赋值(+=、-= 等)和 Python 3.8 新增的海象运算符 :=:

python 复制代码
x = 10
x += 5    # 等价于 x = x + 5  → 15
x -= 3    # 等价于 x = x - 3  → 12
x *= 2    # 等价于 x = x * 2  → 24
x /= 4    # → 6.0
x //= 2   # → 3.0
x **= 2   # → 9.0
x %= 5    # → 4.0

# 海象运算符(Python 3.8+):在表达式中赋值
# 语法: (变量名 := 表达式)
if (n := len("hello")) > 3:
    print(f"字符串长度为 {n},大于3")  # 字符串长度为 5,大于3

# 经典用法:循环中避免重复计算
# 传统写法
import re
while True:
    line = input()
    if not line:
        break
# 海象写法
# while line := input():
#     # 处理 line

🔑 要点总结:海象运算符 := 能在表达式内部完成赋值,常用于 if 和 while 中避免重复计算,如 while (line := file.readline())。

4.6 成员运算符

💡 代码说明:in 和 not in 用于检查元素是否存在于序列(列表、字符串、字典的键等)中:

python 复制代码
# in 和 not in:检查元素是否在序列中
fruits = ["apple", "banana", "orange"]

print("apple" in fruits)       # True
print("grape" not in fruits)   # True
print("e" in "hello")          # True(字符串也是序列)

# 字典中检查键
user = {"name": "Alice", "age": 25}
print("name" in user)          # True
print("Alice" in user)         # False(检查的是键,不是值)
print("Alice" in user.values())  # True

🔑 要点总结:对于字典,in 默认检查键而非值;检查值需要用 .values()。in 对集合的判断是 O(1) 的,非常高效。

4.7 运算符优先级(从高到低)

python 复制代码
# **                    幂运算
# +x, -x, ~x           一元运算符
# *, /, //, %          乘除取余
# +, -                 加减
# <<, >>               位移
# &                    按位与
# ^                    按位异或
# |                    按位或
# ==, !=, >, <, >=, <=, is, is not, in, not in  比较
# not                  逻辑非
# and                  逻辑与
# or                   逻辑或
# :=                   海象运算符

# 不确定时就加括号!
result = (2 + 3) * 4   # 20

5. 字符串详解

5.1 字符串创建

💡 代码说明:Python 提供了四种字符串定义方式:单引号、双引号、三单引号、三双引号,以及原始字符串(r-string)来忽略转义:

python 复制代码
# 四种引号方式
s1 = '单引号字符串'
s2 = "双引号字符串"
s3 = '''三单引号
可以跨行'''
s4 = """三双引号
也可以跨行"""

# 引号嵌套
s5 = "It's a book"       # 双引号内包含单引号
s6 = 'He said "Hello"'   # 单引号内包含双引号
s7 = 'It\'s a book'      # 转义字符

# 原始字符串(raw string):不处理转义
path = r"C:\Users\name\new_folder"
print(path)  # C:\Users\name\new_folder(\n 不会被当成换行)

🔑 要点总结:日常用单/双引号即可;多行文本用三引号;处理 Windows 路径或正则表达式时,原始字符串 r'...' 能省去大量转义的麻烦。

5.2 转义字符

💡 代码说明:反斜杠 \ 用于引入特殊字符,如换行 \n、制表符 \t、引号 " 等:

python 复制代码
print("Hello\nWorld")    # 换行
print("Hello\tWorld")    # 制表符
print("Hello\rWorld")    # 回车
print("He said \"Hi\"")  # 双引号
print('It\'s a book')    # 单引号
print("Path: C:\\data")  # 反斜杠
print("Line1\\\nLine2")  # 续行(行末的反斜杠表示下一行继续)

🔑 要点总结:转义字符让字符串能表达任意特殊字符。需要输出反斜杠本身时用 \,或使用原始字符串 r'...'。

5.3 字符串索引与切片

💡 代码说明:字符串支持索引(单个字符)和切片(子字符串)。语法为 start:stop:step,start 包含、stop 不包含、step 为步长:

python 复制代码
text = "Python编程"

# 索引(从0开始)
print(text[0])     # P
print(text[-1])    # 程  (负数索引从右边开始,-1 是最后一个)
print(text[6])     # 编

# 切片 [start:stop:step]
# start: 起始索引(包含),默认0
# stop:  结束索引(不包含),默认末尾
# step:  步长,默认1
text = "0123456789"

print(text[0:5])     # 01234  索引0到4
print(text[2:7])     # 23456
print(text[:5])      # 01234  省略start,从0开始
print(text[5:])      # 56789  省略stop,到末尾
print(text[::2])     # 02468  步长为2
print(text[::-1])    # 9876543210  反转字符串!
print(text[-3:])     # 789  最后3个字符

🔑 要点总结:切片是 Python 最优雅的特性之一。::-1 反转字符串、-3: 取最后三个字符------简洁而强大。切片操作同样适用于列表和元组。

5.4 字符串常用方法

💡 代码说明:字符串有丰富的方法用于大小写转换、空白处理、查找替换、判断和分割连接。以下是完整的参考示例:

python 复制代码
s = "  Hello, Python World!  "

# --- 大小写转换 ---
print(s.upper())        # "  HELLO, PYTHON WORLD!  "  全大写
print(s.lower())        # "  hello, python world!  "  全小写
print(s.title())        # "  Hello, Python World!  "  每个单词首字母大写
print(s.capitalize())   # "  hello, python world!  "  首字母大写
print(s.swapcase())     # "  hELLO, pYTHON wORLD!  "  大小写翻转

# --- 空白处理 ---
print(s.strip())        # "Hello, Python World!"  去两端空白
print(s.lstrip())       # "Hello, Python World!  "  去左端空白
print(s.rstrip())       # "  Hello, Python World!"  去右端空白

# --- 查找与替换 ---
print(s.find("Python"))     # 9  找到返回索引,找不到返回 -1
print(s.index("Python"))    # 9  找到返回索引,找不到抛出 ValueError
print(s.rfind("o"))         # 16 从右边查找
print(s.count("o"))         # 3  统计出现次数
print(s.replace("World", "大家"))  # "  Hello, Python 大家!  "
print(s.replace("o", "O", 1))      # "  HellO, Python World!  "  只替换1次

# --- 判断方法 ---
print(s.startswith("  He"))  # True  是否以指定前缀开始
print(s.endswith("!  "))     # True  是否以指定后缀结束
print("abc123".isalnum())    # True  是否只含字母和数字
print("abc".isalpha())       # True  是否只含字母
print("123".isdigit())       # True  是否只含数字
print("abc".islower())       # True  是否全小写
print("ABC".isupper())       # True  是否全大写
print("   ".isspace())       # True  是否全空白字符
print("Title".istitle())     # True  是否是标题格式

# --- 分割与连接 ---
csv = "apple,banana,orange"
print(csv.split(","))            # ['apple', 'banana', 'orange']
print(csv.split(",", 1))         # ['apple', 'banana,orange']  只分割1次
print("a b  c".split())          # ['a', 'b', 'c']  默认按空白分割且会合并多个空白

# rsplit:从右边开始分割
print("a,b,c".rsplit(",", 1))    # ['a,b', 'c']

# splitlines:按行分割
multiline = "line1\nline2\nline3"
print(multiline.splitlines())    # ['line1', 'line2', 'line3']
print(multiline.splitlines(keepends=True))  # ['line1\n', 'line2\n', 'line3']

# join:用分隔符连接可迭代对象
words = ["Python", "is", "awesome"]
print(" ".join(words))           # Python is awesome
print("-".join(words))           # Python-is-awesome
print(", ".join(["a", "b", "c"]))  # a, b, c

# partition:分割成三部分 (前, 分隔符, 后)
print("hello world".partition(" "))   # ('hello', ' ', 'world')
# rpartition:从右边开始
print("hello world hello".rpartition(" "))  # ('hello world', ' ', 'hello')

# --- 填充与对齐 ---
print("42".zfill(5))         # 00042  用0填充左侧
print("hi".ljust(10, "-"))   # hi--------  左对齐
print("hi".rjust(10, "-"))   # --------hi  右对齐
print("hi".center(10, "-"))  # ----hi----  居中对齐

🔑 要点总结:这些方法都不会修改原字符串(字符串不可变),而是返回新字符串。特别推荐记住 split()、join()、strip() 和 replace()------它们是日常编程中使用频率最高的字符串方法。

5.5 字符串格式化

💡 代码说明:Python 提供了四种字符串格式化方式。f-string(Python 3.6+)是最推荐的,支持表达式嵌入、格式说明符和调试模式:

python 复制代码
name = "Alice"
age = 25
pi = 3.1415926

# 方法1:f-string(Python 3.6+,推荐!)
print(f"我叫{name},今年{age}岁")
print(f"π ≈ {pi:.2f}")              # π ≈ 3.14
print(f"{name:>10}")                  # '     Alice'  右对齐,宽度10
print(f"{name:*^10}")                 # '**Alice***'  居中,* 填充
print(f"{1000000:,}")                 # 1,000,000  千分位
print(f"{0.25:.1%}")                 # 25.0%  百分比
print(f"{255:#x}")                   # 0xff  十六进制
print(f"{age=}")                     # age=25  (调试模式)

# 方法2:str.format()
print("我叫{},今年{}岁".format(name, age))
print("我叫{0},今年{1}岁,{0}来自北京".format(name, age))  # 按索引
print("我叫{name},今年{age}岁".format(name="Bob", age=30))  # 按名称

# 方法3:% 格式化(旧式,不推荐)
print("我叫%s,今年%d岁" % (name, age))
print("π ≈ %.2f" % pi)

# 方法4:format() 内置函数
print(format(pi, ".2f"))     # 3.14

🔑 要点总结:f-string 不仅最简洁,而且是效率最高的格式化方式。{age=} 调试模式能打印变量名和值,在排查问题时非常方便。

5.6 字符串不可变性

💡 代码说明:字符串是不可变类型------不能通过索引直接修改某个字符。正确的做法是创建新字符串:

python 复制代码
s = "Hello"
# s[0] = "h"   # ❌ TypeError: 'str' object does not support item assignment

# 正确做法:创建新字符串
s = "h" + s[1:]
print(s)  # "hello"

🔑 要点总结:理解不可变性有助于理解 Python 的内存模型:每次修改字符串实际都在创建新对象,频繁拼接时推荐用 join() 而非 + 以获得更好性能。


6. 列表(List)

列表是 Python 中最常用的可变 序列类型,元素可重复,用 [] 表示。

6.1 创建列表

💡 代码说明:列表用 \[\] 或 list() 创建,可以包含任意类型的元素,支持乘法重复和嵌套。但要注意嵌套列表乘法的陷阱:

python 复制代码
# 多种创建方式
empty = []
nums = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, [1, 2]]  # 可以包含不同类型
nested = [[1, 2], [3, 4], [5, 6]]          # 嵌套列表

# 用构造函数创建
chars = list("hello")          # ['h', 'e', 'l', 'l', 'o']
range_list = list(range(5))    # [0, 1, 2, 3, 4]

# 列表乘法
zeros = [0] * 5                # [0, 0, 0, 0, 0]

# ⚠️ 陷阱:嵌套列表乘法
grid = [[0] * 3] * 3  # 看起来是3x3矩阵
grid[0][0] = 1
print(grid)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]  ← 三行指向同一个子列表!

# ✅ 正确做法
grid = [[0] * 3 for _ in range(3)]
grid[0][0] = 1
print(grid)  # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]

🔑 要点总结:嵌套列表乘法 \[0*3]*3 会创建指向同一子列表的多个引用------这是一个经典陷阱!正确的做法是使用列表推导式 \[0*3 for _ in range(3)]。

6.2 访问与切片

💡 代码说明:列表的索引和切片语法与字符串完全一致------正向下标从0开始,负向下标从-1开始:

python 复制代码
fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]

# 索引
print(fruits[0])     # 苹果
print(fruits[-1])    # 西瓜

# 切片(和字符串一样)
print(fruits[1:3])   # ['香蕉', '橙子']
print(fruits[::2])   # ['苹果', '橙子', '西瓜']
print(fruits[::-1])  # ['西瓜', '葡萄', '橙子', '香蕉', '苹果']  反转

🔑 要点总结:列表切片返回新列表,不影响原列表。fruits::-1 可以快速反转列表,而且切片语法适用于所有序列类型。

6.3 修改列表

💡 代码说明:列表是可变的------可以修改单个元素、用切片批量替换、删除元素或清空整个列表:

python 复制代码
fruits = ["苹果", "香蕉", "橙子"]

# 修改元素
fruits[1] = "草莓"
print(fruits)  # ['苹果', '草莓', '橙子']

# 用切片批量修改
fruits[1:3] = ["芒果", "葡萄"]
print(fruits)  # ['苹果', '芒果', '葡萄']

# 删除元素
del fruits[0]
print(fruits)  # ['芒果', '葡萄']

# 清空列表
fruits.clear()
print(fruits)  # []

🔑 要点总结:切片赋值 fruits1:3 = ... 可以一次替换多个元素,替换的元素数量不必相等------这是列表的一大灵活性。

6.4 列表方法

💡 代码说明:列表提供了丰富的方法用于添加(append/extend/insert)、删除(remove/pop)、查找(index/count)和排序(sort):

python 复制代码
# --- 添加元素 ---
nums = [1, 2, 3]

nums.append(4)              # [1, 2, 3, 4]  末尾添加一个元素
nums.extend([5, 6, 7])      # [1, 2, 3, 4, 5, 6, 7]  末尾添加多个元素
nums.insert(0, 0)           # [0, 1, 2, 3, 4, 5, 6, 7]  在指定位置插入

# append vs extend
a = [1, 2]
a.append([3, 4])
print(a)  # [1, 2, [3, 4]]  ← 整个列表作为一个元素
b = [1, 2]
b.extend([3, 4])
print(b)  # [1, 2, 3, 4]    ← 元素逐个加入

# --- 删除元素 ---
nums = [1, 2, 3, 2, 4]

nums.remove(2)     # [1, 3, 2, 4]  删除第一个匹配值(找不到则 ValueError)
popped = nums.pop()  # 弹出并返回最后一个元素 → 4; nums = [1, 3, 2]
popped = nums.pop(1) # 弹出并返回索引1的元素 → 3; nums = [1, 2]
# del nums[0]       # 删除索引0的元素

# --- 查找与统计 ---
nums = [1, 2, 3, 2, 4, 2]

print(nums.index(2))      # 1  第一个出现的索引(找不到则 ValueError)
print(nums.index(2, 2))   # 3  从索引2开始查找
print(nums.count(2))      # 3  统计出现次数
print(len(nums))          # 6  列表长度

# --- 排序 ---
nums = [3, 1, 4, 1, 5, 9]

nums.sort()                    # 原地升序排序 → [1, 1, 3, 4, 5, 9]
nums.sort(reverse=True)        # 原地降序排序 → [9, 5, 4, 3, 1, 1]

# sorted():返回新列表,不改变原列表
original = [3, 1, 4, 1, 5]
sorted_list = sorted(original)  # [1, 1, 3, 4, 5]
print(original)                 # [3, 1, 4, 1, 5]  不变

# 自定义排序:按字符串长度
words = ["apple", "kiwi", "banana", "pear"]
words.sort(key=len)            # ['kiwi', 'pear', 'apple', 'banana']
words.sort(key=lambda w: w[-1])  # 按最后一个字母排序

# --- 其他常用方法 ---
nums = [1, 2, 3]
nums.reverse()           # 原地反转 → [3, 2, 1]
# 或用切片:reversed_list = nums[::-1]

nums_copy = nums.copy()  # 浅拷贝
# 等价于 nums_copy = nums[:] 或 list(nums)

🔑 要点总结:注意 append 和 extend 的关键区别:append 把参数整体作为一个元素加入,extend 则逐个加入。sort() 原地排序,sorted() 返回新列表。

6.5 浅拷贝 vs 深拷贝

💡 代码说明:嵌套列表的拷贝需要特别注意------浅拷贝只复制外层引用,深拷贝才会递归复制所有层级:

python 复制代码
import copy

original = [[1, 2], [3, 4]]

# 浅拷贝:只复制外层
shallow = original.copy()      # 或 original[:] 或 list(original)
shallow[0][0] = 99
print(original)  # [[99, 2], [3, 4]]  ← 受影响!

# 深拷贝:递归复制所有层级
deep = copy.deepcopy(original)
deep[0][0] = 100
print(original)  # [[99, 2], [3, 4]]  ← 不受影响

🔑 要点总结:对于嵌套结构,使用 copy.deepcopy() 才能做到真正独立的复制。但深拷贝性能开销较大,只在确实需要时使用。

6.6 enumerate 与 zip

💡 代码说明:enumerate() 同时获取索引和元素值,zip() 将多个列表打包成一对对元组,是遍历中最常用的两个内置函数:

python 复制代码
fruits = ["苹果", "香蕉", "橙子"]

# enumerate: 同时获取索引和值
for i, fruit in enumerate(fruits):
    print(f"{i}: {fruit}")
# 0: 苹果
# 1: 香蕉
# 2: 橙子

for i, fruit in enumerate(fruits, start=1):
    print(f"{i}: {fruit}")
# 1: 苹果
# 2: 香蕉
# 3: 橙子

# zip: 同时遍历多个列表
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]

for name, age in zip(names, ages):
    print(f"{name} 今年 {age} 岁")

# zip 会在最短的列表结束时停止
a = [1, 2, 3]
b = ['a', 'b']
print(list(zip(a, b)))  # [(1, 'a'), (2, 'b')]

# 需要严格匹配长度?用 zip_longest
from itertools import zip_longest
print(list(zip_longest(a, b, fillvalue=None)))
# [(1, 'a'), (2, 'b'), (3, None)]

🔑 要点总结:enumerate(seq, start=1) 让序号从1开始,zip() 按最短列表截断,需要严格匹配时用 zip_longest()。


7. 元组(Tuple)

元组是不可变 序列类型,用 () 表示。一旦创建,不可修改。

7.1 创建元组

💡 代码说明:元组用 () 或 tuple() 创建。特别注意:单元素元组必须在元素后加逗号,否则会被当成普通括号表达式:

python 复制代码
t1 = (1, 2, 3)
t2 = 1, 2, 3          # 逗号创建,括号可省略(不推荐省略)
t3 = (1,)             # ⚠️ 单元素元组必须有逗号!
t4 = (1)              # 这不是元组,是整数1!
t5 = ()               # 空元组
t6 = tuple([1, 2, 3]) # 从其他序列创建
t7 = tuple("hello")   # ('h', 'e', 'l', 'l', 'o')

🔑 要点总结:(1,) 是元组,(1) 只是整数 1------逗号才是元组的真正标识,括号在很多情况下可以省略。

7.2 元组的不可变性

💡 代码说明:元组创建后不能修改------不能赋值、不能添加、不能删除。但如果元组包含可变对象(如列表),该可变对象的内容可以改变:

python 复制代码
t = (1, 2, 3)
# t[0] = 10          # ❌ TypeError: 'tuple' object does not support item assignment
# t.append(4)         # ❌ AttributeError: 'tuple' object has no attribute 'append'

# 【注意】如果元组包含可变对象,可变对象的内容可以改!
t = ([1, 2], 3)
t[0].append(3)
print(t)  # ([1, 2, 3], 3)  元组本身不变,但内部的列表变了

🔑 要点总结:元组的不可变指的是引用不可变------元组中存储的每个元素的引用不能改变,但如果这个引用指向一个可变对象,该对象本身还是可以修改的。

7.3 元组解包

💡 代码说明:序列解包是 Python 的一大特色,可以一次性将元组(或列表)的元素赋值给多个变量。* 可以收集多余元素:

python 复制代码
# 序列解包(unpacking)
point = (3, 4)
x, y = point
print(x, y)  # 3 4

# 多变量同时赋值(本质是元组打包再解包)
a, b = 1, 2

# 交换变量(Python 特有写法)
a, b = b, a

# 星号表达式:收集多余元素
first, *middle, last = [1, 2, 3, 4, 5]
print(first)   # 1
print(middle)  # [2, 3, 4]
print(last)    # 5

head, *tail = [1, 2, 3]
print(head)    # 1
print(tail)    # [2, 3]

# 忽略不需要的值(_ 作为占位符)
_, _, z = (1, 2, 3)
print(z)  # 3

🔑 要点总结:解包让交换变量 a, b = b, a 变得异常简洁,*middle 收集中间元素、_ 忽略不关心的值------这些都是 Pythonic 的编码风格。

7.4 为什么使用元组?

  1. 不可变性保证数据不会被意外修改
  2. 作为字典的键(列表不能)
  3. 性能略优于列表
  4. 语义表达:暗示这是一组不应修改的数据(如坐标、RGB值)

💡 代码说明:元组不只是「不可变的列表」------它还可以作为字典键、利用 namedtuple 创建轻量数据类:

python 复制代码
# 元组作为字典的键
scores = {("Alice", "Math"): 95, ("Alice", "English"): 88}
print(scores[("Alice", "Math")])  # 95

# namedtuple:带名称的元组
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y)   # 3 4
print(p[0], p[1]) # 3 4(仍支持索引访问)
# p.x = 5         # ❌ 不可修改(和普通元组一样)

🔑 要点总结:当你有一组不应被修改的数据(如坐标、RGB值、数据库记录)时,元组通过不可变性传达了一组数据的语义。namedtuple 则是轻量数据类的绝佳选择。


8. 字典(Dictionary)

字典是键值对 的集合,键必须不可变(字符串、数字、元组),{key: value} 表示。Python 3.7+ 字典保持插入顺序。

8.1 创建与访问

💡 代码说明:字典是 Python 的核心数据结构,用 {} 或 dict() 创建,有六种创建方式。访问时推荐使用 get() 而非直接索引:

python 复制代码
# 创建字典
d1 = {"name": "Alice", "age": 25, "city": "北京"}
d2 = dict(name="Bob", age=30)           # 键为字符串时可用
d3 = dict([("name", "Charlie"), ("age", 35)])  # 从键值对序列创建
d4 = dict(zip(["a", "b", "c"], [1, 2, 3]))     # {'a': 1, 'b': 2, 'c': 3}
d5 = dict.fromkeys(["a", "b", "c"], 0)         # {'a': 0, 'b': 0, 'c': 0}
d6 = {}                            # 空字典

# 访问值
print(d1["name"])                  # Alice
# print(d1["gender"])              # ❌ KeyError(键不存在时)

# get():键不存在时返回默认值(推荐!)
print(d1.get("name"))              # Alice
print(d1.get("gender", "未知"))     # 未知(键不存在,返回默认值)

# setdefault():如果键不存在,设置默认值并返回
d1.setdefault("score", 0)          # 不存在则设置为 0
print(d1)  # {..., 'score': 0}

🔑 要点总结:get(key, default) 在键不存在时返回默认值而不抛异常;setdefault() 则在键不存在时同时设置默认值------两者都能避免 KeyError。

8.2 修改字典

💡 代码说明:字典支持添加、修改、更新、删除和合并操作。Python 3.9+ 还引入了 | 和 |= 字典合并运算符:

python 复制代码
user = {"name": "Alice", "age": 25}

# 添加/修改
user["city"] = "上海"      # 添加新键
user["age"] = 26           # 修改已有键
print(user)  # {'name': 'Alice', 'age': 26, 'city': '上海'}

# update():批量更新
user.update({"age": 27, "email": "alice@example.com"})
print(user)  # {'name': 'Alice', 'age': 27, 'city': '上海', 'email': 'alice@example.com'}
# 也可以用 update(key=value, ...)
user.update(phone="123456", country="中国")

# 删除
del user["phone"]              # 删除指定键(不存在则 KeyError)
popped = user.pop("email")     # 弹出并返回值(不存在则 KeyError)
popped = user.pop("email", None)  # 不存在时返回默认值
last = user.popitem()          # 弹出最后一个键值对(Python 3.7+ LIFO)
user.clear()                   # 清空

# 字典合并(Python 3.9+)
d1 = {"a": 1, "b": 2}
d2 = {"b": 3, "c": 4}
merged = d1 | d2               # {'a': 1, 'b': 3, 'c': 4}  后者覆盖前者
d1 |= d2                       # 原地合并

🔑 要点总结:pop(key, default) 比 del 更安全。d1 | d2 合并字典时,相同键的值会被 d2 覆盖,非常直观。

8.3 遍历字典

💡 代码说明:字典支持遍历键(.keys())、值(.values())和键值对(.items())。遍历时不要直接修改字典大小:

python 复制代码
user = {"name": "Alice", "age": 25, "city": "北京"}

# 遍历键
for key in user:
    print(key)

for key in user.keys():
    print(key)

# 遍历值
for value in user.values():
    print(value)

# 遍历键值对
for key, value in user.items():
    print(f"{key}: {value}")

# 安全遍历并修改:先收集再修改
# ❌ 不要在遍历时修改字典大小
# for key in user:
#     del user[key]  # RuntimeError!

# ✅ 先收集要删除的键
to_delete = [key for key in user if key.startswith("a")]
for key in to_delete:
    del user[key]

🔑 要点总结:遍历字典时修改其大小会引发 RuntimeError。正确的做法是先收集要删除的键,循环结束后再批量删除。

8.4 字典推导式

💡 代码说明:字典推导式用 {key: value for ...} 语法快速创建或变换字典,支持条件过滤:

python 复制代码
# {key_expr: value_expr for item in iterable}

# 平方数字典
squares = {x: x**2 for x in range(5)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 交换键值
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
print(swapped)  # {1: 'a', 2: 'b', 3: 'c'}

# 条件过滤
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}
print(even_squares)  # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

🔑 要点总结:字典推导式比传统的 for 循环更简洁,是 Pythonic 风格的体现。常见用法包括创建映射表、交换键值、过滤特定项等。

8.5 defaultdict 与 Counter

💡 代码说明:collections 模块的 defaultdict 和 Counter 是字典的强力扩展,能极大简化分组和统计类代码:

python 复制代码
# --- defaultdict: 带默认值的字典 ---
from collections import defaultdict

# 按类型统计(无需检查键是否存在)
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
word_count = defaultdict(int)  # int() 返回 0
for word in words:
    word_count[word] += 1  # 不存在自动初始化为 0
print(word_count)  # defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})

# 分组
groups = defaultdict(list)
for word in words:
    groups[len(word)].append(word)
print(groups)  # defaultdict(<class 'list'>, {5: ['apple', 'apple'], 6: ['banana', 'orange', 'banana']})

# --- Counter: 计数器 ---
from collections import Counter

cnt = Counter(words)
print(cnt)                   # Counter({'apple': 3, 'banana': 2, 'orange': 1})
print(cnt.most_common(2))    # [('apple', 3), ('banana', 2)]  前2个最常见元素
print(cnt["grape"])          # 0  (不存在的键返回 0)

🔑 要点总结:defaultdict 省去了检查键是否存在的样板代码;Counter 让频率统计变为一行代码。两者都是日常编程中的高频工具。


9. 集合(Set)

集合是无序、不重复 元素的集合,用 {} 表示。元素必须不可变。

9.1 创建集合

💡 代码说明:集合用 {} 或 set() 创建,元素自动去重且无序。注意:set() 创建空集合,{} 创建的是空字典:

python 复制代码
# 创建集合
s1 = {1, 2, 3, 4}
s2 = set([1, 2, 2, 3, 3, 4])  # {1, 2, 3, 4}  自动去重
s3 = set("hello")               # {'h', 'e', 'l', 'o'}  l只出现一次
s4 = set()                      # 空集合(不能用 {},那是空字典)

# 集合推导式
squares = {x**2 for x in range(10)}  # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}

🔑 要点总结:集合支持推导式,和列表推导式语法类似。因为元素不可变,集合中不能包含列表或字典。

9.2 集合操作

💡 代码说明:集合支持数学中的交、并、差、对称差运算,以及子集/超集判断。运算符(| & - ^)和方法效果相同:

python 复制代码
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# --- 数学集合运算 ---
print(a | b)         # {1, 2, 3, 4, 5, 6}  并集(a.union(b))
print(a & b)         # {3, 4}              交集(a.intersection(b))
print(a - b)         # {1, 2}              差集(a.difference(b))
print(b - a)         # {5, 6}
print(a ^ b)         # {1, 2, 5, 6}        对称差集(a.symmetric_difference(b))

# --- 集合关系 ---
print(a <= b)        # False  a 是 b 的子集?(a.issubset(b))
print(a >= b)        # False  a 是 b 的超集?(a.issuperset(b))
print(a.isdisjoint({7, 8}))  # True  没有交集?

# --- 修改方法 ---
s = {1, 2, 3}
s.add(4)             # {1, 2, 3, 4}  添加元素
s.update([5, 6])     # {1, 2, 3, 4, 5, 6}  批量添加
s.remove(3)          # 删除(元素不存在则 KeyError)
s.discard(3)         # 删除(元素不存在也不报错!推荐)
s.pop()              # 随机弹出一个元素(空集合则 KeyError)
s.clear()            # 清空

# 集合的原地运算(以 _update 结尾)
a = {1, 2, 3}
a.intersection_update({2, 3, 4})  # a 变为 {2, 3}

🔑 要点总结:原地运算方法(如 intersection_update())以 _update 结尾,直接修改原集合。discard() 比 remove() 更安全------元素不存在时不会报错。

9.3 集合的应用场景

💡 代码说明:集合在实际应用中最常见的三个场景:去重、快速成员检查(O(1))、集合运算(找交集/差集)。frozenset 是不可变版本,可作为字典键:

python 复制代码
# 1. 去重
numbers = [1, 2, 2, 3, 3, 3, 4]
unique = list(set(numbers))  # [1, 2, 3, 4](顺序不保证)

# 保持顺序的去重(Python 3.7+)
unique_ordered = list(dict.fromkeys(numbers))  # [1, 2, 3, 4]

# 2. 成员检查(O(1) 比列表的 O(n) 快)
large_set = set(range(1000000))
print(500000 in large_set)   # 非常快

# 3. 找共同元素
list1 = [1, 2, 3, 4]
list2 = [3, 4, 5, 6]
common = set(list1) & set(list2)  # {3, 4}

# 4. Frozen Set(不可变集合,可作为字典的键)
fs = frozenset([1, 2, 3])
# fs.add(4)     # ❌ AttributeError
d = {fs: "frozen"}  # ✅ 可以作为键

🔑 要点总结:当需要频繁判断元素是否存在时,将列表转为集合可以大幅提升性能(从 O(n) 降到 O(1))。保持顺序的去重可以用 dict.fromkeys() 实现。


10. 条件判断

10.1 if / elif / else

💡 代码说明:条件判断是程序流程控制的基础。Python 使用 if-elif-else 结构,注意每个条件后必须有冒号:

python 复制代码
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}")  # 成绩等级: B

# 简写(适合简单情况)
x = 10
status = "正数" if x > 0 else "非正数"
# 等价于:
# if x > 0:
#     status = "正数"
# else:
#     status = "非正数"

# 嵌套的三元运算符
y = 5
result = "大" if y > 10 else ("小" if y > 0 else "零或负")
print(result)  # 小

🔑 要点总结:三元表达式 a if condition else b 适合简单的条件赋值。Python 3.10 之前用 elif 链替代 switch-case。

10.2 真值测试

Python 中以下值被视为 False(假值),其余为 True

💡 代码说明 :Python 中以下值被视为假:False、None、各种零值、空序列和空集合。其余所有值都是真。自定义类的真值由 bool () 或 len() 决定:

python 复制代码
# 假值列表
falsy = [
    False,      # 布尔假
    None,       # 空值
    0,          # 整数零
    0.0,        # 浮点零
    0j,         # 复数零
    "",         # 空字符串
    [],         # 空列表
    (),         # 空元组
    {},         # 空字典
    set(),      # 空集合
    range(0),   # 空范围
]

for value in falsy:
    print(f"{value!r:10} -> {bool(value)}")

# 真值测试在条件判断中的应用
name = ""
if name:
    print(f"你好,{name}")
else:
    print("名称为空")  # 输出这个

# 自定义类的真值:实现 __bool__() 或 __len__() 方法
class MyCollection:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)

c = MyCollection([])
print(bool(c))  # False

🔑 要点总结:利用真值测试可以写出简洁的代码:if name: 代替 if name != '',if items: 代替 if len(items) > 0。这是 Pythonic 风格的体现。

10.3 match / case(Python 3.10+)

💡 代码说明:Python 3.10 引入的结构化模式匹配 match-case 比传统的 if-elif 更强大------支持多值匹配、序列解构、字典匹配和守卫条件:

python 复制代码
# 类似其他语言的 switch
def describe(status_code):
    match status_code:
        case 200:
            return "成功"
        case 301 | 302:            # 多个值
            return "重定向"
        case 404:
            return "未找到"
        case 500:
            return "服务器错误"
        case _:                     # 默认(通配符)
            return "未知状态"


# 结构匹配(更强大的用法)
def process_point(point):
    match point:
        case (0, 0):
            return "原点"
        case (0, y):
            return f"Y 轴, y={y}"
        case (x, 0):
            return f"X 轴, x={x}"
        case (x, y) if x == y:     # 带守卫条件
            return f"在第一/三象限对角线上"
        case (x, y):
            return f"坐标 ({x}, {y})"

print(process_point((0, 0)))    # 原点
print(process_point((0, 5)))    # Y 轴, y=5
print(process_point((3, 3)))    # 在第一/三象限对角线上
print(process_point((2, 5)))    # 坐标 (2, 5)

# 匹配列表结构
def analyze_list(lst):
    match lst:
        case []:
            return "空列表"
        case [first, *rest]:
            return f"首元素: {first}, 剩余: {rest}"

print(analyze_list([1, 2, 3]))  # 首元素: 1, 剩余: [2, 3]

# 匹配字典结构
def analyze_dict(d):
    match d:
        case {"name": name, "age": age}:
            return f"姓名: {name}, 年龄: {age}"
        case {"name": name}:
            return f"姓名: {name}, 年龄未知"
        case _:
            return "未知格式"

🔑 要点总结:match-case 不是简单的 switch 替代品------它是真正的模式匹配,能同时匹配结构并绑定变量。特别适合解析复杂数据结构、实现状态机等场景。


11. 循环语句

11.1 for 循环

💡 代码说明:for 循环是 Python 最常用的循环方式,可以遍历列表、字典、range 对象等任何可迭代对象:

python 复制代码
# 遍历列表
fruits = ["苹果", "香蕉", "橙子"]
for fruit in fruits:
    print(fruit)

# 遍历字典
user = {"name": "Alice", "age": 25}
for key, value in user.items():
    print(f"{key}: {value}")

# 遍历范围
for i in range(5):        # 0, 1, 2, 3, 4
    print(i)

for i in range(2, 7):     # 2, 3, 4, 5, 6  (start, stop)
    print(i)

for i in range(0, 10, 2): # 0, 2, 4, 6, 8   (start, stop, step)
    print(i)

for i in range(10, 0, -1):  # 10, 9, 8, ..., 1  倒序
    print(i)

# 用 enumerate 获取索引
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")

🔑 要点总结:range(start, stop, step) 生成整数序列,enumerate() 同时获取索引和值。Python 的 for 循环简洁而强大。

11.2 while 循环

💡 代码说明:while 循环在条件为真时反复执行。适合不知道循环次数的场景,或者实现无限循环+break 的模式:

python 复制代码
# 基本 while 循环
count = 0
while count < 5:
    print(count)
    count += 1

# 使用标志的 while 循环
running = True
attempts = 0
while running:
    attempts += 1
    # ... 处理逻辑 ...
    if attempts >= 3:
        running = False

# 无限循环 + break
while True:
    user_input = input("输入 'quit' 退出: ")
    if user_input == "quit":
        break
    print(f"你输入了: {user_input}")

🔑 要点总结:注意防止无限循环------确保循环条件最终会变为 False,或者在循环体内使用 break 退出。while True + break 是一种常见的交互式程序模式。

11.3 循环控制

💡 代码说明:break 立即退出循环,continue 跳过当前迭代,pass 什么都不做只作占位。for...else 在循环正常结束(非 break 退出)时执行:

python 复制代码
# break:立即退出整个循环
for i in range(10):
    if i == 5:
        break
    print(i, end=" ")  # 0 1 2 3 4

print()  # 换行

# continue:跳过当前迭代,进入下一次
for i in range(10):
    if i % 2 == 0:
        continue
    print(i, end=" ")  # 1 3 5 7 9

print()

# pass:占位符,什么都不做
for i in range(5):
    if i == 2:
        pass  # 以后再来处理
    print(i, end=" ")  # 0 1 2 3 4

print()

# for ... else:循环正常结束时执行 else
for i in range(5):
    print(i)
else:
    print("循环正常结束")  # 会执行

for i in range(5):
    if i == 3:
        break
else:
    print("不会执行")  # ← break 后不执行 else

# while ... else 同样适用
n = 0
while n < 5:
    n += 1
else:
    print("while 正常结束")  # 执行

🔑 要点总结:for...else 是 Python 特有的语法,特别适合搜索-未找到的模式:在循环中找到目标就 break,else 块处理未找到的情况。

11.4 循环技巧

💡 代码说明:利用 zip() 同时遍历多个序列、reversed() 反向遍历、sorted() 按排序遍历------这些技巧让你的循环代码更优雅:

python 复制代码
# 遍历两个序列(zip)
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
for name, score in zip(names, scores):
    print(f"{name}: {score}分")

# 反向遍历
for i in reversed(range(5)):
    print(i, end=" ")  # 4 3 2 1 0
print()

# 排序遍历
nums = [3, 1, 4, 1, 5]
for n in sorted(nums):
    print(n, end=" ")  # 1 1 3 4 5
print()

for n in sorted(nums, reverse=True):
    print(n, end=" ")  # 5 4 3 1 1
print()

🔑 要点总结:掌握这些内置函数,可以大幅减少循环中的索引操作,写出更 Pythonic 的代码。


12. 函数

12.1 定义与调用

💡 代码说明 :使用 def 关键字定义函数,return 返回值。函数文档字符串(docstring)用三引号书写,可通过 doc 或 help() 查看:

python 复制代码
# 基本函数
def greet(name):
    """向指定的人打招呼(函数文档字符串)"""
    return f"你好,{name}!"

print(greet("Alice"))  # 你好,Alice!
print(greet.__doc__)   # 向指定的人打招呼(函数文档字符串)
help(greet)            # 返回更详细的信息

# 多个参数
def add(a, b):
    return a + b

# 无返回值:默认返回 None
def log_message(msg):
    print(f"[LOG] {msg}")
    # 隐式 return None

result = log_message("test")
print(result)  # None

🔑 要点总结:Python 函数如果没有显式 return,默认返回 None。养成写 docstring 的好习惯,它能被 IDE 和 help() 识别。

12.2 参数类型

💡 代码说明:Python 函数支持四种参数类型:位置参数、默认参数、*args(可变位置参数)、**kwargs(可变关键字参数)。参数顺序必须严格按照这个顺序:

python 复制代码
# --- 位置参数 ---
def power(base, exp):
    return base ** exp

print(power(2, 3))         # 8(位置参数)
print(power(exp=3, base=2)) # 8(关键字参数)

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

print(greet("Alice"))              # 你好,Alice!
print(greet("Bob", "早上好"))       # 早上好,Bob!

# ⚠️ 默认参数的陷阱:不要用可变对象作为默认值!
def bad_append(item, lst=[]):
    lst.append(item)
    return lst

print(bad_append(1))  # [1]
print(bad_append(2))  # [1, 2]  ← 累积了!默认列表是函数定义时创建的,每次调用共享

# ✅ 正确做法
def good_append(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

# --- 可变参数 *args(位置参数) ---
def my_sum(*args):
    """接受任意数量的位置参数"""
    total = 0
    for num in args:
        total += num
    return total

print(my_sum(1, 2))          # 3
print(my_sum(1, 2, 3, 4, 5)) # 15
print(my_sum())               # 0

# --- 可变参数 **kwargs(关键字参数) ---
def print_info(**kwargs):
    """接受任意数量的关键字参数"""
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=25, city="北京")
# name: Alice
# age: 25
# city: 北京

# --- 所有参数类型组合 ---
def all_params(pos1, pos2, *args, default="默认值", **kwargs):
    """
    参数顺序(必须严格遵守):
    1. 位置参数(必填)
    2. *args(可变位置)
    3. 默认参数(关键字参数)
    4. **kwargs(可变关键字)
    """
    print(f"pos1: {pos1}")
    print(f"pos2: {pos2}")
    print(f"args: {args}")
    print(f"default: {default}")
    print(f"kwargs: {kwargs}")

all_params(1, 2, 3, 4, 5, default="自定义", name="Alice", age=25)

🔑 要点总结:最常见的陷阱是可变默认参数(如 def f(lst=\[\]))------默认值在函数定义时只创建一次,多次调用会共享!正确做法是用 None 作默认值,函数内再初始化。

12.3 仅限关键字参数

💡 代码说明:在参数列表中使用 * 可以强制后面的参数必须以关键字形式传递,提高代码可读性。/ 则相反,强制前面的参数只能按位置传递:

python 复制代码
# * 之后的参数必须以关键字形式传递
def config(host, *, port=8080, debug=False):
    print(f"host={host}, port={port}, debug={debug}")

config("localhost")                  # host=localhost, port=8080, debug=False
config("localhost", port=3000)       # ✅
config("localhost", debug=True)      # ✅
# config("localhost", 3000)          # ❌ TypeError: 位置参数太多了

# 仅限位置参数(Python 3.8+)/ 之前的参数只能按位置传递
def func(a, b, /, c, d):
    """
    a, b: 仅限位置参数(不能以关键字形式传递)
    c, d: 可以是位置或关键字
    """
    print(f"a={a}, b={b}, c={c}, d={d}")

func(1, 2, 3, 4)          # ✅
func(1, 2, c=3, d=4)      # ✅
# func(a=1, b=2, c=3, d=4)  # ❌ TypeError: a 和 b 仅限位置

🔑 要点总结:仅限关键字参数让 API 设计更清晰------调用者必须显式写出参数名,减少参数顺序出错的可能。

12.4 参数解包

💡 代码说明:* 将列表/元组解包为位置参数,** 将字典解包为关键字参数。两种解包可以组合使用:

python 复制代码
# 列表/元组解包为位置参数
def add(a, b, c):
    return a + b + c

nums = [1, 2, 3]
print(add(*nums))   # 6  等价于 add(1, 2, 3)
print(add(*(4, 5, 6)))  # 15

# 字典解包为关键字参数
def greet(name, age):
    return f"{name} 今年 {age} 岁"

info = {"name": "Alice", "age": 25}
print(greet(**info))  # Alice 今年 25 岁

# 组合使用
def full_func(a, b, c, d, e):
    return a + b + c + d + e

nums1 = [1, 2]
nums2 = {"d": 4, "e": 5}
print(full_func(*nums1, 3, **nums2))  # 15

🔑 要点总结:参数解包让你能动态地构建函数调用,在编写通用工具函数和转发调用时特别有用。

12.5 作用域与 global / nonlocal

💡 代码说明:Python 变量遵循 LEGB 查找规则(Local -> Enclosing -> Global -> Built-in)。使用 global 在函数内修改全局变量,nonlocal 修改外层函数的变量:

python 复制代码
# --- LEGB 规则(变量查找顺序)---
# L: Local      --- 函数内部
# E: Enclosing  --- 外层函数内部
# G: Global     --- 模块级别
# B: Built-in   --- 内置命名空间

x = "global"      # 全局变量

def outer():
    x = "enclosing"  # 闭包变量

    def inner():
        x = "local"  # 局部变量
        print(f"inner: {x}")  # local

    inner()
    print(f"outer: {x}")  # enclosing

outer()
print(f"global: {x}")  # global

# --- global 关键字:在函数内修改全局变量 ---
count = 0

def increment():
    global count
    count += 1

increment()
print(count)  # 1

# --- nonlocal 关键字:修改外层函数的变量 ---
def make_counter():
    count = 0

    def counter():
        nonlocal count  # 使用外层的 count
        count += 1
        return count

    return counter

c = make_counter()
print(c())  # 1
print(c())  # 2
print(c())  # 3

🔑 要点总结:尽量避免滥用 global------它会让代码难以理解和测试。nonlocal 在闭包中很有用,能让内部函数修改外层函数的局部变量。

12.6 闭包

💡 代码说明:闭包是函数式编程的核心概念------内部函数记住并访问其创建时的外层变量,即使外层函数已经返回:

python 复制代码
# 闭包:内部函数记住了其创建时的环境
def make_multiplier(factor):
    def multiplier(x):
        return x * factor  # factor 被"记住"了
    return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))  # 10
print(triple(5))  # 15

# 查看闭包变量
print(double.__closure__[0].cell_contents)  # 2

🔑 要点总结 :闭包是实现装饰器、工厂函数和回调的基础。closure 属性可以查看闭包捕获的变量,有助于调试。


13. Lambda 表达式

Lambda 是创建小型匿名函数的语法,只能包含单个表达式。

💡 代码说明:闭包是函数式编程的核心概念------内部函数记住并访问其创建时的外层变量,即使外层函数已经返回:

python 复制代码
# 语法: lambda 参数: 表达式

# 基本用法
add = lambda a, b: a + b
print(add(3, 5))  # 8

# 等价于
def add(a, b):
    return a + b

# --- 常见使用场景 ---
# 1. sort() 的 key 参数
students = [
    {"name": "Alice", "score": 85},
    {"name": "Bob", "score": 92},
    {"name": "Charlie", "score": 78},
]
students.sort(key=lambda s: s["score"], reverse=True)
print(students)
# [{'name': 'Bob', 'score': 92}, {'name': 'Alice', 'score': 85}, {'name': 'Charlie', 'score': 78}]

# 2. filter():过滤序列
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even = list(filter(lambda x: x % 2 == 0, nums))
print(even)  # [2, 4, 6, 8, 10]

# 3. map():映射序列
squared = list(map(lambda x: x ** 2, nums))
print(squared)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 4. reduce():累积(需要导入)
from functools import reduce
total = reduce(lambda acc, x: acc + x, nums)
print(total)  # 55

# --- Lambda vs 列表推导式 ---
# 大部分情况下列表推导式更 Pythonic:

# 用 filter + lambda
even1 = list(filter(lambda x: x % 2 == 0, nums))

# 用列表推导式(推荐!)
even2 = [x for x in nums if x % 2 == 0]

# 用 map + lambda
squared1 = list(map(lambda x: x ** 2, nums))

# 用列表推导式(推荐!)
squared2 = [x ** 2 for x in nums]

🔑 要点总结 :闭包是实现装饰器、工厂函数和回调的基础。closure 属性可以查看闭包捕获的变量,有助于调试。


14. 列表推导式与生成器表达式

14.1 列表推导式

💡 代码说明:列表推导式是 Python 的标志性特性,用 expression for item in iterable if condition 语法在一行内完成映射和过滤:

python 复制代码
# 基本语法: [expression for item in iterable if condition]

nums = list(range(10))

# 基本
squares = [x ** 2 for x in nums]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 带条件过滤
even_squares = [x ** 2 for x in nums if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]

# if-else 表达式(注意位置)
parity = ["even" if x % 2 == 0 else "odd" for x in nums]
print(parity)  # ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']

# 嵌套推导式
# 展平二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 等价于
flattened = []
for row in matrix:
    for num in row:
        flattened.append(num)

# 笛卡尔积
pairs = [(x, y) for x in range(3) for y in range(3)]
print(pairs)
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

🔑 要点总结:列表推导式比传统的 for+append 更简洁高效(底层是 C 实现的)。但不要过度嵌套------超过两层的推导式会牺牲可读性。

14.2 生成器表达式

💡 代码说明:生成器表达式语法和列表推导式一样,只是用 () 替代 \[\]。它惰性求值,适合处理海量数据:

python 复制代码
# 语法和列表推导式一样,只是用 () 而不是 []
# 优势:惰性求值,节省内存(适合海量数据)

# 列表推导式:立即生成所有数据到内存
squares_list = [x ** 2 for x in range(1000000)]  # 占用大量内存

# 生成器表达式:按需生成,几乎不占内存
squares_gen = (x ** 2 for x in range(1000000))    # 轻量级

# 迭代生成器
for i in squares_gen:
    print(i)
    if i > 50:
        break  # 只计算需要的部分

# sum/max/min 等可以直接接受生成器表达式
total = sum(x ** 2 for x in range(1000000))  # 不需要再写一层括号
print(total)

🔑 要点总结:当数据量很大(百万级)时,生成器表达式几乎不占内存。传递给 sum()、max() 等函数时,可以省略额外的括号。

14.3 字典推导式

💡 代码说明:字典推导式 {key: value for ...} 快速创建或变换字典,是从两个列表创建映射表的绝佳方式:

python 复制代码
# {key_expr: value_expr for item in iterable if condition}

# 基本
squares = {x: x**2 for x in range(5)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 过滤
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}
print(even_squares)  # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

# 从两个列表创建字典
keys = ["name", "age", "city"]
values = ["Alice", 25, "北京"]
info = {k: v for k, v in zip(keys, values)}
print(info)  # {'name': 'Alice', 'age': 25, 'city': '北京'}

🔑 要点总结:结合 zip() 使用字典推导式,可以一行代码将两个列表变为字典。支持条件过滤,语法与列表推导式一致。

14.4 集合推导式

💡 代码说明:集合推导式 {expr for ...} 自动去重,本质上就是花括号包裹的列表推导式:

python 复制代码
# {expression for item in iterable if condition}

# 基本
squares_set = {x**2 for x in range(-5, 6)}
print(squares_set)  # {0, 1, 4, 9, 16, 25}  自动去重

# 字符串去重并保持顺序(用字典推导式)
text = "abracadabra"
unique_chars = "".join(dict.fromkeys(text))
print(unique_chars)  # "abrcd"

🔑 要点总结:集合推导式在需要去重的同时做变换时非常有用,比先建列表再转集合更高效。


15. 迭代器与生成器

15.1 可迭代对象 vs 迭代器

💡 代码说明:可迭代对象(Iterable)可以放进 for 循环,迭代器(Iterator)则是记住遍历位置的懒加载对象。用 iter() 从可迭代对象获取迭代器,next() 逐个取值:

python 复制代码
# 可迭代对象 (Iterable):实现了 __iter__() 方法
# 迭代器 (Iterator):实现了 __iter__() 和 __next__() 方法

# 判断类型
from collections.abc import Iterable, Iterator

lst = [1, 2, 3]
print(isinstance(lst, Iterable))    # True  (列表是可迭代对象)
print(isinstance(lst, Iterator))    # False (列表不是迭代器)

it = iter(lst)
print(isinstance(it, Iterator))     # True

# iter() 和 next()
it = iter(lst)
print(next(it))  # 1
print(next(it))  # 2
print(next(it))  # 3
# print(next(it))  # StopIteration 异常

🔑 要点总结:迭代器只能前进不能后退,遍历结束后再调用 next() 会抛出 StopIteration。这种一次性的特性让迭代器非常节省内存。

15.2 生成器函数(yield)

💡 代码说明:使用 yield 关键字的函数是生成器------每次 yield 暂停并返回值,下次调用 next() 时从暂停处继续。yield from 可以委托给子生成器:

python 复制代码
# yield:暂停函数,返回值,下次从暂停处继续

# 基本生成器
def countdown(n):
    print(f"开始倒计时: {n}")
    while n > 0:
        yield n
        n -= 1
    print("结束!")

for num in countdown(3):
    print(f"倒计时: {num}")
# 开始倒计时: 3
# 倒计时: 3
# 倒计时: 2
# 倒计时: 1
# 结束!

# --- 无限序列生成器 ---
def fibonacci():
    """生成无限的斐波那契数列"""
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
print([next(fib) for _ in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# --- yield from: 委托给子生成器 ---
def flat_list(nested):
    """递归展平任意嵌套的列表"""
    for item in nested:
        if isinstance(item, list):
            yield from flat_list(item)  # Python 3.3+
        else:
            yield item

nested = [1, [2, 3], [4, [5, 6]]]
print(list(flat_list(nested)))  # [1, 2, 3, 4, 5, 6]

# --- 管道生成器 ---
def read_lines(filename):
    """逐行读取文件"""
    with open(filename, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()

def filter_comments(lines):
    """过滤注释行"""
    for line in lines:
        if not line.startswith("#"):
            yield line

# 使用
# valid_lines = filter_comments(read_lines("config.txt"))
# for line in valid_lines:
#     print(line)

🔑 要点总结:生成器是实现流式处理、惰性计算的利器。yield from 在需要递归处理嵌套结构时特别有用,比如展平任意深度的嵌套列表。

15.3 send() 与双向通信

💡 代码说明:生成器不仅可以通过 yield 向外产出值,还可以通过 send() 接收外部传入的值,实现双向通信(协程的基础):

python 复制代码
# 生成器不仅可以产出值,还可以通过 send() 接收值
def running_average():
    """维持运行平均值的协程"""
    total = 0
    count = 0
    average = None
    while True:
        # yield 的返回值是通过 send() 传入的值
        x = yield average
        if x is None:
            break
        total += x
        count += 1
        average = total / count

avg = running_average()
next(avg)  # 启动生成器(推进到第一个 yield)
print(avg.send(10))  # 10.0
print(avg.send(20))  # 15.0
print(avg.send(30))  # 20.0
avg.close()  # 关闭生成器

🔑 要点总结:send() 让生成器升级为协程。首次调用必须用 next() 或 send(None) 启动。用完后调用 close() 关闭生成器释放资源。


16. 装饰器

装饰器是一种在不修改原函数代码的情况下增强其功能的设计模式。

16.1 函数是一等公民

💡 代码说明:在 Python 中,函数是一等公民------可以赋值给变量、作为参数传递、作为返回值返回。这是理解装饰器的基础:

python 复制代码
# Python 中函数是对象,可以赋给变量、作为参数、作为返回值
def greet(name):
    return f"Hello, {name}"

# 赋值给变量
say_hello = greet
print(say_hello("Alice"))  # Hello, Alice

# 作为参数传递
def call_twice(func, arg):
    return func(arg), func(arg)

print(call_twice(greet, "Bob"))  # ('Hello, Bob', 'Hello, Bob')

# 作为返回值(内部函数/闭包)
def make_greeting(prefix):
    def greeting(name):
        return f"{prefix}, {name}"
    return greeting

hello = make_greeting("Hello")
print(hello("Charlie"))  # Hello, Charlie

🔑 要点总结:正因为函数是一等公民,Python 才能优雅地实现装饰器、回调、高阶函数等函数式编程模式。

16.2 基本装饰器

💡 代码说明:装饰器本质上是一个接受函数、返回新函数的包装器。使用 @decorator 语法糖,@functools.wraps 保留原函数的元信息:

python 复制代码
import functools
import time

# --- 装饰器模式 ---
def timer(func):
    """记录函数执行时间的装饰器"""
    @functools.wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} 执行耗时: {elapsed:.4f} 秒")
        return result
    return wrapper

# 使用装饰器(语法糖)
@timer
def slow_function(n):
    """一个耗时的函数"""
    total = sum(i ** 2 for i in range(n))
    return total

# 等价于: slow_function = timer(slow_function)
slow_function(1000000)  # slow_function 执行耗时: 0.1234 秒

print(slow_function.__name__)  # slow_function(被 @wraps 保留了)
print(slow_function.__doc__)   # 一个耗时的函数

# 不带 @wraps 的话 __name__ 会变成 "wrapper"

🔑 要点总结 :@functools.wraps(func) 至关重要------没有它,被装饰函数的 namedoc 会变成 wrapper 的信息。这是装饰器最常见也最容易忘记的细节。

16.3 带参数的装饰器

💡 代码说明:带参数的装饰器本质上是装饰器工厂------外层函数接收参数,返回真正的装饰器:

python 复制代码
# 装饰器工厂:返回装饰器的函数
def retry(max_attempts=3, delay=1):
    """重试装饰器:函数失败后自动重试"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts:
                        raise  # 最后一次尝试仍然失败则抛出异常
                    print(f"[重试 {attempt}/{max_attempts}] {e}")
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

@retry(max_attempts=3, delay=0.5)
def unstable_network_call():
    import random
    if random.random() < 0.7:
        raise ConnectionError("网络连接失败")
    return "成功"

# 尝试调用
# result = unstable_network_call()
# print(result)

🔑 要点总结:装饰器工厂比基本装饰器多一层嵌套。理解装饰器本质上是函数调用这一点,就不会对三层嵌套感到困惑。

16.4 常用装饰器

💡 代码说明:Python 内置了三个常用装饰器:@staticmethod(静态方法)、@classmethod(类方法)和 @property(属性访问控制)。@property 让你能用属性语法调用方法,同时保留验证逻辑:

python 复制代码
# --- @staticmethod: 静态方法 ---
class MyClass:
    @staticmethod
    def helper():
        """不需要访问 self 或 cls"""
        return "I'm a static method"

# --- @classmethod: 类方法 ---
class Person:
    species = "Homo sapiens"

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

    @classmethod
    def from_birth_year(cls, name, birth_year):
        """工厂方法"""
        return cls(f"{name} ({2026 - birth_year}岁)")

    @classmethod
    def get_species(cls):
        return cls.species

# --- @property: 属性装饰器 ---
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        """getter"""
        return self._radius

    @radius.setter
    def radius(self, value):
        """setter: 设置时添加验证"""
        if value < 0:
            raise ValueError("半径不能为负数")
        self._radius = value

    @radius.deleter
    def radius(self):
        """deleter"""
        del self._radius

    @property
    def area(self):
        """只读计算属性"""
        return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.radius)  # 5(调用 getter,不需要 c.radius())
print(c.area)    # 78.53975
c.radius = 10    # 调用 setter(带验证)
# c.radius = -1 # 抛出 ValueError

🔑 要点总结:@property 是 Python 实现 getter/setter 的优雅方式------外部代码用 obj.attr 访问,内部自动调用相应方法。@classmethod 常用于实现工厂方法。


17. 模块与包

17.1 模块导入

💡 代码说明:Python 的模块系统通过 import 导入外部代码,支持多种导入方式和条件导入:

python 复制代码
# --- 导入方式 ---
# 1. 导入整个模块
import math
print(math.sqrt(16))  # 4.0

# 2. 导入模块并起别名
import numpy as np
# arr = np.array([1, 2, 3])

# 3. 从模块导入特定函数/类
from math import sqrt, pi
print(sqrt(25))   # 5.0
print(pi)         # 3.141592...

# 4. 从模块导入全部(不推荐,会污染命名空间)
# from math import *

# 5. 条件导入
try:
    import ujson as json  # 如果装了更快的版本
except ImportError:
    import json            # 使用标准库

# --- 模块搜索路径 ---
import sys
print(sys.path)  # 模块搜索路径列表

# --- 查看模块内容 ---
import math
print(dir(math))     # 列出模块所有属性和方法
print(math.__doc__)  # 模块文档
# help(math)         # 在 REPL 中查看帮助

🔑 要点总结:推荐用 import module 或 from module import specific_item,避免 from module import *(会污染命名空间)。条件导入让代码在不同环境下自动适配。

17.2 包的结构

💡 代码说明 :包是包含 init .py 的目录,用于组织多个模块。init .py 中可以定义 all 控制导出、导入子模块方便用户使用:

python 复制代码
# 包是一个包含 __init__.py 文件的目录

# 典型包结构:
# my_package/
# ├── __init__.py          # 包的初始化文件
# ├── core.py              # 核心模块
# ├── utils/
# │   ├── __init__.py
# │   ├── helpers.py
# │   └── validators.py
# └── tests/
#     ├── __init__.py
#     └── test_core.py

# --- __init__.py 示例 ---
"""
my_package 包初始化
"""
# 控制 from my_package import * 导出的内容
__all__ = ["core_func", "utils"]

# 可在此处导入子模块,方便用户
from .core import core_func
from . import utils

# 包的版本
__version__ = "1.0.0"

🔑 要点总结 :即使 init.py 为空文件也没关系,它的存在标志着该目录是一个 Python 包(Python 3.3+ 的隐式命名空间包除外)。

17.3 相对导入与绝对导入

💡 代码说明:在包内部,使用 .(当前目录)和 ...(上级目录)进行相对导入。绝对导入从项目根目录开始:

python 复制代码
# --- 在包内部,使用相对导入 ---
# 文件: my_package/utils/helpers.py

# 相对导入: . 表示当前目录,.. 表示上级目录
# from . import validators         # 同目录
# from ..core import core_func     # 上级目录的 core 模块

# 绝对导入: 从顶层开始
# from my_package.core import core_func

🔑 要点总结:包内部推荐使用相对导入(更灵活、不依赖项目名),对外公开的 API 使用绝对导入。

17.4 常用标准库模块

💡 代码说明:Python 标准库极其丰富,覆盖了操作系统接口、日期时间、JSON 处理、正则表达式、高级集合类和迭代工具等:

python 复制代码
# --- os: 操作系统接口 ---
import os
print(os.name)              # 'nt' (Windows) 或 'posix' (Linux/Mac)
print(os.getcwd())          # 当前工作目录
# os.listdir(".")           # 列出目录内容
# os.mkdir("new_dir")      # 创建目录
# os.remove("file.txt")    # 删除文件
print(os.path.join("dir", "subdir", "file.txt"))  # 跨平台路径拼接
print(os.path.exists("some_file.txt"))            # 文件是否存在
print(os.path.dirname("/path/to/file.txt"))       # /path/to
print(os.path.basename("/path/to/file.txt"))      # file.txt

# --- sys: 系统相关 ---
import sys
print(sys.version)          # Python 版本
print(sys.argv)             # 命令行参数
# sys.exit(0)               # 退出程序

# --- random: 随机数 ---
import random
print(random.random())            # [0.0, 1.0) 的随机浮点数
print(random.randint(1, 10))      # [1, 10] 的随机整数
print(random.choice([1, 2, 3]))   # 随机选择一个元素
print(random.sample(range(100), 5))  # 随机选择5个不重复的元素
random.shuffle([1, 2, 3, 4, 5])     # 原地打乱列表
random.seed(42)                      # 设置随机种子(可复现)

# --- datetime: 日期时间 ---
from datetime import datetime, timedelta, date

now = datetime.now()
print(now)                          # 2026-07-21 14:30:00.123456
print(now.strftime("%Y-%m-%d %H:%M:%S"))  # 2026-07-21 14:30:00
parsed = datetime.strptime("2026-07-21", "%Y-%m-%d")
print(parsed)  # 2026-07-21 00:00:00

tomorrow = now + timedelta(days=1)
last_week = now - timedelta(weeks=1)
print(tomorrow)
print(last_week)

# --- json: JSON 处理 ---
import json

data = {"name": "Alice", "age": 25, "skills": ["Python", "JavaScript"]}
json_str = json.dumps(data, indent=2, ensure_ascii=False)
print(json_str)
parsed = json.loads(json_str)
print(parsed["name"])  # Alice

# --- re: 正则表达式 ---
import re

text = "联系方式: alice@example.com, bob_123@gmail.com"
emails = re.findall(r'[\w.+-]+@[\w-]+\.[\w.-]+', text)
print(emails)  # ['alice@example.com', 'bob_123@gmail.com']

# --- collections: 更多集合类型 ---
from collections import Counter, defaultdict, OrderedDict, deque
# Counter / defaultdict 已在前面介绍

# deque: 双端队列(高效的两端操作)
dq = deque([1, 2, 3])
dq.append(4)       # 右边添加 → deque([1, 2, 3, 4])
dq.appendleft(0)   # 左边添加 → deque([0, 1, 2, 3, 4])
dq.pop()           # 右边弹出 → 4
dq.popleft()       # 左边弹出 → 0

# --- itertools: 迭代器工具 ---
import itertools

# 无限迭代器
print(list(itertools.islice(itertools.count(10, 2), 5)))  # [10, 12, 14, 16, 18]
print(list(itertools.islice(itertools.cycle("ABC"), 6)))  # ['A', 'B', 'C', 'A', 'B', 'C']

# 组合迭代器
print(list(itertools.product("AB", "12")))     # [('A', '1'), ('A', '2'), ('B', '1'), ('B', '2')]
print(list(itertools.permutations("ABC", 2)))  # 排列: 6种
print(list(itertools.combinations("ABC", 2)))  # 组合: 3种
print(list(itertools.combinations_with_replacement("ABC", 2)))  # 有放回组合

# 过滤迭代器
print(list(itertools.compress("ABCD", [1, 0, 1, 0])))  # ['A', 'C']
print(list(itertools.dropwhile(lambda x: x < 3, [1, 2, 3, 4, 1, 2])))  # [3, 4, 1, 2]
print(list(itertools.takewhile(lambda x: x < 3, [1, 2, 3, 4, 1, 2])))  # [1, 2]

# 分组
data = [("a", 1), ("a", 2), ("b", 3), ("b", 4)]
for key, group in itertools.groupby(data, lambda x: x[0]):
    print(key, "->", list(group))
# a -> [('a', 1), ('a', 2)]
# b -> [('b', 3), ('b', 4)]

# --- functools: 高阶函数 ---
from functools import reduce, partial, lru_cache

# partial: 固定部分参数
def power(base, exp):
    return base ** exp

square = partial(power, exp=2)  # 固定 exp=2
cube = partial(power, exp=3)    # 固定 exp=3
print(square(5))  # 25
print(cube(5))    # 125

# lru_cache: 缓存函数结果(记忆化)
@lru_cache(maxsize=128)
def fibonacci(n):
    """计算斐波那契数(带缓存)"""
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(100))  # 354224848179261915075(瞬间计算)
print(fibonacci.cache_info())  # CacheInfo(hits=98, misses=101, maxsize=128, currsize=101)

🔑 要点总结:标准库是 Python 的一大优势------自带电池。itertools 和 collections 是提高代码效率的两大利器,pathlib 是处理文件路径的现代化选择,建议优先使用。


18. 文件操作

18.1 基本读写

💡 代码说明:使用 open() 打开文件,配合 with 语句确保自动关闭。支持多种读取方式(全部、分块、单行、所有行、逐行迭代)和文件模式:

python 复制代码
# --- 写入文件 ---
# 模式: 'w' (写), 'a' (追加), 'x' (排他创建)
with open("example.txt", "w", encoding="utf-8") as f:
    f.write("Hello, World!\n")
    f.write("这是第二行。\n")
    f.writelines(["第三行\n", "第四行\n"])

# --- 读取文件 ---
# 模式: 'r' (读,默认)
with open("example.txt", "r", encoding="utf-8") as f:
    # 方式1: 读取全部
    content = f.read()
    print(content)

with open("example.txt", "r", encoding="utf-8") as f:
    # 方式2: 读取指定字符数
    chunk = f.read(10)
    print(chunk)  # Hello, Wor

with open("example.txt", "r", encoding="utf-8") as f:
    # 方式3: 读取单行
    line = f.readline()
    print(line)  # Hello, World!

with open("example.txt", "r", encoding="utf-8") as f:
    # 方式4: 读取所有行到列表
    lines = f.readlines()
    print(lines)  # ['Hello, World!\n', '这是第二行。\n', ...]

with open("example.txt", "r", encoding="utf-8") as f:
    # 方式5: 逐行迭代(推荐,内存友好)
    for line in f:
        print(line.strip())

# --- 文件模式 ---
# 'r'  - 只读(文件必须存在)
# 'w'  - 只写(文件存在则清空,不存在则创建)
# 'a'  - 追加(文件存在则在末尾添加)
# 'x'  - 排他创建(文件已存在则失败)
# 'r+' - 读写
# 'b'  - 二进制模式(如 'rb', 'wb')
# 't'  - 文本模式(默认,如 'rt', 'wt')

🔑 要点总结:逐行迭代(for line in f)是最推荐的大文件读取方式------不会一次性将整个文件加载到内存。始终使用 encoding='utf-8' 避免编码问题。

18.2 上下文管理器(with 语句)

💡 代码说明:with 语句确保资源(如文件)在使用后自动释放,即使发生异常也不会泄漏。它等价于 try/finally 的语法糖:

python 复制代码
# with 语句确保文件正确关闭,即使发生异常
# 等价于:
# f = open("file.txt")
# try:
#     data = f.read()
# finally:
#     f.close()

with open("example.txt", "r", encoding="utf-8") as f:
    data = f.read()
# 文件在这里自动关闭(离开 with 块时)

# 同时操作多个文件
with open("source.txt", "r", encoding="utf-8") as src, \
     open("dest.txt", "w", encoding="utf-8") as dst:
    dst.write(src.read())

🔑 要点总结:任何需要获取-使用-释放模式的资源(文件、锁、连接)都应使用 with 语句,这是 Python 资源管理的最佳实践。

18.3 文件与目录操作

💡 代码说明:os 和 shutil 模块提供了文件和目录的增删改查操作。Python 3.4+ 的 pathlib.Path 提供了更面向对象的路径操作方式:

python 复制代码
import os
import shutil

# --- 文件操作 ---
# 检查是否存在
print(os.path.exists("example.txt"))   # True/False
print(os.path.isfile("example.txt"))   # 是文件?
print(os.path.isdir("some_dir"))       # 是目录?

# 获取文件信息
print(os.path.getsize("example.txt"))  # 文件大小(字节)
print(os.path.getmtime("example.txt")) # 最后修改时间戳

# 重命名/移动
os.rename("old_name.txt", "new_name.txt")
# shutil.move("source.txt", "destination/")

# 复制文件
# shutil.copy("source.txt", "destination/")

# 删除文件
os.remove("example.txt")

# --- 目录操作 ---
# 创建目录
os.mkdir("single_dir")                       # 创建单层目录(父目录必须存在)
os.makedirs("parent/child/grandchild")       # 递归创建多层目录

# 列出目录内容
entries = os.listdir(".")
for entry in entries:
    print(entry)

# 递归遍历
for root, dirs, files in os.walk("."):
    for file in files:
        print(os.path.join(root, file))

# 删除目录
# os.rmdir("empty_dir")         # 只能删除空目录
# shutil.rmtree("dir_path")     # 递归删除整个目录树

# --- pathlib(Python 3.4+,面向对象的路径操作,推荐!)---
from pathlib import Path

p = Path("data") / "subdir" / "file.txt"
print(p)                            # data\subdir\file.txt  (Windows)
print(p.name)                       # file.txt
print(p.stem)                       # file
print(p.suffix)                     # .txt
print(p.parent)                     # data\subdir

# 创建目录
Path("my_folder").mkdir(exist_ok=True)
Path("nested/deep/folder").mkdir(parents=True, exist_ok=True)

# 读写(Python 3.5+)
# text = Path("file.txt").read_text(encoding="utf-8")
# Path("output.txt").write_text("Hello", encoding="utf-8")

# 遍历目录
# for file in Path(".").glob("*.py"):
#     print(file)
# for file in Path(".").rglob("*.py"):  # 递归
#     print(file)

🔑 要点总结:pathlib 比传统的 os.path 更直观------用 / 拼接路径、.read_text() 一行读取文件。建议在新项目中使用 pathlib 替代字符串路径操作。

18.4 CSV 与 JSON 文件

💡 代码说明:Python 内置的 csv 和 json 模块让结构化数据的读写变得简单。csv.DictReader 用列名访问数据,json.dump/load 处理 JSON:

python 复制代码
import csv
import json

# --- CSV 读取 ---
with open("data.csv", "r", encoding="utf-8", newline="") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# CSV 写入
with open("output.csv", "w", encoding="utf-8", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["姓名", "年龄", "城市"])     # 标题行
    writer.writerows([                          # 多行数据
        ["Alice", 25, "北京"],
        ["Bob", 30, "上海"],
    ])

# DictReader / DictWriter(使用列名)
# with open("data.csv", "r", encoding="utf-8") as f:
#     reader = csv.DictReader(f)
#     for row in reader:
#         print(row["姓名"])

# --- JSON 读写 ---
data = {"users": [{"name": "Alice", "age": 25}, {"name": "Bob", "age": 30}]}

# 写入
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

# 读取
with open("data.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)
    print(loaded["users"][0]["name"])  # Alice

🔑 要点总结:处理 CSV 时建议使用 DictReader/DictWriter,代码更易读。JSON 的 ensure_ascii=False 能正确显示中文,indent 参数让输出格式化。


19. 异常处理

19.1 try / except / else / finally

💡 代码说明:异常处理的核心结构:try 块放可能出错的代码,except 捕获特定异常,else 在无异常时运行,finally 无论如何都执行:

python 复制代码
# --- 基本结构 ---
try:
    num = int(input("请输入一个整数: "))
    result = 100 / num
except ValueError as e:       # 捕获特定异常
    print(f"格式错误: {e}")
except ZeroDivisionError:
    print("不能除以零!")
except (TypeError, OverflowError) as e:  # 捕获多种异常
    print(f"类型或溢出错误: {e}")
except Exception as e:        # 捕获所有异常(基类)
    print(f"未知错误: {e}")
else:                         # 无异常时执行
    print(f"结果是: {result}")
finally:                      # 无论如何都执行
    print("清理资源...")

# --- 常见异常类型 ---
# ValueError        值错误
# TypeError         类型错误
# KeyError          字典键不存在
# IndexError        索引越界
# AttributeError    对象没有该属性
# NameError         变量名不存在
# FileNotFoundError 文件未找到
# IOError           I/O 错误
# ZeroDivisionError 除以零
# ImportError       导入失败
# RuntimeError      运行时错误
# StopIteration     迭代结束
# AssertionError    断言失败

🔑 要点总结:永远捕获最具体的异常类型,不要使用裸露的 except:(它会捕获 SystemExit 和 KeyboardInterrupt)。常见的异常类型有十多种,IDE 会帮你补全。

19.2 自定义异常

💡 代码说明:通过继承 Exception 创建自定义异常,可以携带更多上下文信息。raise ... from ... 建立异常链,保留原始错误信息:

python 复制代码
# 自定义异常类
class ValidationError(Exception):
    """数据验证异常"""

    def __init__(self, field, message="验证失败"):
        self.field = field
        self.message = message
        super().__init__(f"{field}: {message}")


class InsufficientFundsError(Exception):
    """余额不足异常"""

    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(
            f"余额不足:当前余额 {balance},需要 {amount},"
            f"差额 {amount - balance}"
        )


# 使用自定义异常
def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

try:
    withdraw(100, 200)
except InsufficientFundsError as e:
    print(e)

# 异常链(raise ... from ...)
def process_file(filepath):
    try:
        with open(filepath, "r") as f:
            return f.read()
    except FileNotFoundError as e:
        raise RuntimeError(f"处理文件 {filepath} 失败") from e

🔑 要点总结:自定义异常让错误信息更具体、更容易调试。异常链 from e 保留了原始错误的完整堆栈,是调试复杂系统的重要工具。

19.3 最佳实践

💡 代码说明:Python 社区推崇 EAFP(请求原谅比请求许可更容易)风格------先尝试,失败再处理------而不是 LBYL(三思而后行)的预检查风格:

python 复制代码
# ❌ 不要这样做:泛泛捕获所有异常
# try:
#     risky_operation()
# except:  # 捕获包括 SystemExit、KeyboardInterrupt 等
#     pass  # 静默吞掉异常

# ✅ 正确做法
# try:
#     risky_operation()
# except Exception as e:  # 明确捕获 Exception
#     logger.error(f"操作失败: {e}", exc_info=True)
#     raise  # 重新抛出或适当处理

# ✅ EAFP: Easier to Ask for Forgiveness than Permission
# 请求原谅比请求许可更容易(Python 风格)
# 先尝试,失败再处理
# def get_item(lst, index):
#     try:
#         return lst[index]
#     except IndexError:
#         return None

# ✅ LBYL: Look Before You Leap(其他语言风格)
# def get_item(lst, index):
#     if 0 <= index < len(lst):
#         return lst[index]
#     return None

🔑 要点总结:EAFP 利用异常处理替代条件检查,代码更简洁,且在并发环境下更安全。但要注意:只捕获预期的异常,不要让 except 掩盖 bug。


20. 面向对象编程

20.1 类与对象

💡 代码说明 :Python 使用 class 定义类,init 初始化实例,self 指向实例本身。类属性被所有实例共享,双下划线前缀触发名称改写实现私有:

python 复制代码
class Person:
    """人类"""

    # 类属性(所有实例共享)
    species = "Homo sapiens"

    # 实例初始化方法
    def __init__(self, name, age):
        # 实例属性
        self.name = name    # 公有属性
        self.age = age
        self._secret = None  # 约定:受保护属性(单下划线)
        self.__private = 0   # 名称改写:_Person__private

    # 实例方法
    def introduce(self):
        return f"我叫{self.name},今年{self.age}岁。"

    # 字符串表示(给开发者看的)
    def __repr__(self):
        return f"Person(name='{self.name}', age={self.age})"

    # 字符串表示(给用户看的,print 调用)
    def __str__(self):
        return f"{self.name} ({self.age}岁)"


# 创建实例
alice = Person("Alice", 25)
bob = Person("Bob", 30)

print(alice.introduce())           # 我叫Alice,今年25岁。
print(alice)                       # Alice (25岁)  调用 __str__
print(repr(alice))                 # Person(name='Alice', age=25)  调用 __repr__

# 访问类属性
print(Person.species)              # Homo sapiens
print(alice.species)               # Homo sapiens(实例也可访问)

# 名称改写
# print(alice.__private)           # ❌ AttributeError
print(alice._Person__private)      # 0(可以绕过但不推荐)

🔑 要点总结:Python 没有真正的私有属性------双下划线前缀只是名称改写为 _ClassName__attr,这是一种约定而非强制。单下划线前缀 _attr 表示受保护,更多是给开发者看的提示。

20.2 继承

💡 代码说明:Python 支持单继承和多继承。子类可以重写父类方法,使用 isinstance() 和 issubclass() 检查继承关系:

python 复制代码
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "汪汪!"

    def wag_tail(self):
        return f"{self.name} 摇尾巴"

class Cat(Animal):
    def speak(self):
        return "喵喵!"

dog = Dog("旺财")
cat = Cat("咪咪")

print(dog.name, "说:", dog.speak())       # 旺财 说: 汪汪!
print(cat.name, "说:", cat.speak())       # 咪咪 说: 喵喵!
print(dog.wag_tail())                     # 旺财 摇尾巴

# 检查继承关系
print(isinstance(dog, Dog))       # True
print(isinstance(dog, Animal))    # True
print(isinstance(dog, Cat))       # False
print(issubclass(Dog, Animal))    # True

# 多重继承
class Flyable:
    def fly(self):
        return "飞行中..."

class Swimmable:
    def swim(self):
        return "游泳中..."

class Duck(Animal, Flyable, Swimmable):
    def speak(self):
        return "嘎嘎!"

duck = Duck("唐老鸭")
print(duck.speak())  # 嘎嘎!
print(duck.fly())    # 飞行中...
print(duck.swim())   # 游泳中...

# MRO(方法解析顺序)
print(Duck.__mro__)  # 查看方法搜索顺序

🔑 要点总结:多重继承在需要混入多个功能时很方便(如 Duck 同时继承 Animal、Flyable、Swimmable),但不要过度使用。MRO(方法解析顺序)决定了多继承时方法的查找路径。

20.3 super() 与父类调用

💡 代码说明 :super() 用于调用父类的方法,在 init 和方法重写中特别常用。它遵循 MRO 顺序,在多重继承下也能正确工作:

python 复制代码
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def work(self):
        return f"{self.name} 正在工作"

    def get_details(self):
        return f"Name: {self.name}, Salary: {self.salary}"

class Manager(Employee):
    def __init__(self, name, salary, department):
        # 调用父类的 __init__
        super().__init__(name, salary)
        self.department = department

    def work(self):
        # 调用父类方法并扩展
        base_result = super().work()
        return f"{base_result},管理 {self.department} 部门"

    def get_details(self):
        return f"{super().get_details()}, Dept: {self.department}"


mgr = Manager("Alice", 15000, "技术部")
print(mgr.work())          # Alice 正在工作,管理 技术部 部门
print(mgr.get_details())   # Name: Alice, Salary: 15000, Dept: 技术部

🔑 要点总结:重写父类方法时,通常应先调用 super().method(),然后再添加子类的特定逻辑------这样可以确保父类的初始化或预处理不被遗漏。

20.4 魔术方法(Magic / Dunder Methods)

💡 代码说明:魔术方法(双下划线包围的方法)让自定义类支持运算符重载、迭代、比较、上下文管理等语言特性。以下 Vector 类展示了常用的魔术方法:

python 复制代码
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # 字符串表示
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

    # 算术运算
    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 __rmul__(self, scalar):
        """右侧乘法(scalar * vector)"""
        return self.__mul__(scalar)

    def __neg__(self):
        return Vector(-self.x, -self.y)

    def __abs__(self):
        """向量的模"""
        return (self.x ** 2 + self.y ** 2) ** 0.5

    # 比较运算
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __lt__(self, other):
        """按模长比较"""
        return abs(self) < abs(other)

    # 使对象可迭代
    def __iter__(self):
        return iter([self.x, self.y])

    # 使对象可用 in 检查
    def __contains__(self, value):
        return value in (self.x, self.y)

    # 使对象可调用
    def __call__(self):
        """返回向量的模"""
        return abs(self)

    # 属性访问
    def __getitem__(self, index):
        if index == 0: return self.x
        if index == 1: return self.y
        raise IndexError("索引只能是 0 或 1")

    def __len__(self):
        return 2  # 总是返回 2

    # 上下文管理器
    def __enter__(self):
        print(f"进入 Vector 上下文: {self}")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"退出 Vector 上下文: {self}")

# --- 使用 ---
v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1 + v2)         # Vector(4, 6)
print(v1 - v2)         # Vector(2, 2)
print(v1 * 3)          # Vector(9, 12)
print(3 * v1)          # Vector(9, 12)(__rmul__)
print(-v1)             # Vector(-3, -4)
print(abs(v1))         # 5.0
print(v1 == Vector(3, 4))  # True
print(v1 < Vector(1, 1))   # False (5.0 > 1.414)
# 迭代
for coord in v1:
    print(coord)       # 3  4
print(3 in v1)         # True
print(v1())            # 5.0(可调用)
print(v1[0], v1[1])    # 3 4
print(len(v1))         # 2

# 上下文管理器
with Vector(5, 12) as v:
    print(f"处理中: {v}")
# 进入 Vector 上下文: Vector(5, 12)
# 处理中: Vector(5, 12)
# 退出 Vector 上下文: Vector(5, 12)

🔑 要点总结 :魔术方法是 Python 数据模型的精髓------掌握了它们,你的类就能像内置类型一样自然地被使用。repr (给开发者)和 str(给用户)的区别是面试常考题。

20.5 @dataclass 数据类

💡 代码说明 :Python 3.7+ 的 @dataclass 装饰器自动生成 initrepreq 等方法,大幅减少样板代码:

python 复制代码
from dataclasses import dataclass, field
from typing import List

# Python 3.7+ 的 dataclass 自动生成 __init__、__repr__、__eq__ 等方法
@dataclass
class Point:
    x: float
    y: float

    # 不需要手写 __init__ 和 __repr__!
    # __eq__ 也自动生成

    def distance_to_origin(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5


p1 = Point(3.0, 4.0)
p2 = Point(3.0, 4.0)
print(p1)                          # Point(x=3.0, y=4.0)
print(p1 == p2)                    # True(自动比较字段值)
print(p1.distance_to_origin())     # 5.0

# 更复杂的配置
@dataclass
class Student:
    name: str
    age: int
    grades: List[float] = field(default_factory=list)  # 默认值使用 factory
    active: bool = True

    def average_grade(self):
        if not self.grades:
            return 0.0
        return sum(self.grades) / len(self.grades)


student = Student(name="Alice", age=20, grades=[85.0, 92.0, 78.0])
print(student)                     # Student(name='Alice', age=20, grades=[85.0, 92.0, 78.0], active=True)
print(student.average_grade())     # 85.0

🔑 要点总结 :对于主要存储数据的类,@dataclass 是极佳选择。field(default_factory=list) 正确处理了可变默认值的问题,比手写 init 更安全。


21. 常用内置函数

21.1 类型相关

💡 代码说明:这些内置函数帮助检查类型(type/isinstance)、获取对象信息(id/dir/help):

python 复制代码
# type() - 获取类型
print(type(42))              # <class 'int'>

# isinstance() - 类型检查
print(isinstance(42, int))   # True

# id() - 获取对象内存地址
x = [1, 2, 3]
print(id(x))                 # 某个整数地址

# dir() - 列出属性和方法
print(dir([]))               # 列表的所有方法

# help() - 查看帮助
# help(str)                  # 在 REPL 中

🔑 要点总结:dir() 在探索陌生模块或对象时极其有用;id() 帮助理解对象标识(配合 is 使用)。

21.2 数学运算

💡 代码说明:Python 内置了常用数学函数:abs(绝对值)、round(四舍五入)、max/min(最值,支持 key 参数)、sum(求和)、pow(幂运算)、divmod(商和余数):

python 复制代码
# abs() - 绝对值
print(abs(-5))               # 5

# round() - 四舍五入
print(round(3.14159, 2))    # 3.14

# max() / min()
print(max(1, 5, 3))         # 5
print(min([3, 1, 4, 1, 5])) # 1

# max 的 key 参数
words = ["apple", "kiwi", "banana", "pear"]
print(max(words, key=len))   # banana

# sum()
print(sum([1, 2, 3, 4, 5])) # 15
print(sum([1, 2, 3], 10))   # 16(初始值 10)

# pow()
print(pow(2, 8))             # 256
print(pow(2, 8, 1000))       # 256(取模)

# divmod()
print(divmod(10, 3))         # (3, 1)

🔑 要点总结:max/min 的 key 参数非常强大------按长度找最长单词、按价格找最贵商品,一行搞定。sum 的第二个参数是初始值,默认为 0。

21.3 序列操作

💡 代码说明:序列操作内置函数:len(长度)、sorted(排序返回新列表)、reversed(反向)、enumerate(枚举)、zip(打包)、filter(过滤)、map(映射)、any/all(逻辑判断):

python 复制代码
# len() - 长度
print(len("hello"))          # 5
print(len([1, 2, 3]))        # 3

# sorted() - 排序(返回新列表)
nums = [3, 1, 4, 1, 5]
print(sorted(nums))          # [1, 1, 3, 4, 5]
print(sorted(nums, reverse=True))  # [5, 4, 3, 1, 1]
print(nums)                  # [3, 1, 4, 1, 5](原列表不变)

# reversed() - 反向迭代器
for i in reversed([1, 2, 3]):
    print(i)                 # 3, 2, 1

# range() - 范围
print(list(range(5)))        # [0, 1, 2, 3, 4]

# enumerate() - 枚举
for i, v in enumerate(['a', 'b', 'c'], start=1):
    print(f"{i}: {v}")       # 1: a, 2: b, 3: c

# zip() - 打包
a = [1, 2, 3]
b = ['a', 'b', 'c']
print(list(zip(a, b)))       # [(1, 'a'), (2, 'b'), (3, 'c')]

# filter() - 过滤
nums = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens)                 # [2, 4, 6]

# map() - 映射
squared = list(map(lambda x: x**2, nums))
print(squared)               # [1, 4, 9, 16, 25, 36]

# any() / all()
print(any([False, False, True]))   # True(至少一个为真)
print(all([True, True, False]))    # False(必须全部为真)

🔑 要点总结:sorted() 不改变原序列,any() 和 all() 在条件检查中非常实用。filter 和 map 返回迭代器(不是列表),需要用 list() 转换或直接遍历。

21.4 输入输出

💡 代码说明:print() 的 sep 和 end 参数可以定制输出格式,file 参数可以将输出重定向到文件:

python 复制代码
# print() - 打印
print("Hello", "World", sep=" | ", end="!!!\n")
# Hello | World!!!

# 写入文件
# with open("log.txt", "w") as f:
#     print("错误信息", file=f)

# input() - 用户输入
# name = input("请输入姓名: ")
# print(f"你好,{name}")

# open() - 打开文件
# f = open("file.txt", "r", encoding="utf-8")

🔑 要点总结:print() 的可定制性远超想象------sep 控制分隔符、end 控制结尾字符、file 可重定向输出。input() 读取用户输入,总是返回字符串。

21.5 其他常用函数

💡 代码说明:其他高频函数包括:hasattr/getattr/setattr/delattr(动态操作属性)、callable(判断可调用)、ord/chr(字符编码转换)、bin/oct/hex(进制转换)、eval/exec(动态执行):

python 复制代码
# --- 对象相关 ---
# hasattr() / getattr() / setattr() / delattr()
class Person:
    def __init__(self, name):
        self.name = name

p = Person("Alice")
print(hasattr(p, "name"))          # True
print(getattr(p, "name"))           # "Alice"
print(getattr(p, "age", "未知"))    # "未知"(默认值)
setattr(p, "age", 25)              # 等价于 p.age = 25
delattr(p, "age")                  # 等价于 del p.age

# callable() - 判断是否可调用
print(callable(print))             # True
print(callable("hello"))           # False

# --- 字符串转换 ---
# repr() / ascii()
print(repr("Hello\nWorld"))        # "Hello\nWorld"(开发者表示)
print(ascii("你好"))               # '你好'

# ord() / chr() - 字符与 Unicode 码点
print(ord('A'))                    # 65
print(ord('中'))                   # 20013
print(chr(65))                     # 'A'
print(chr(20013))                  # '中'

# bin() / oct() / hex() - 进制转换
print(bin(10))                     # '0b1010'
print(oct(10))                     # '0o12'
print(hex(255))                    # '0xff'

# --- 迭代工具 ---
# iter() / next()
it = iter([1, 2, 3])
print(next(it))                    # 1

# --- 编译/执行 ---
# compile() / exec() / eval() - 动态执行代码(注意安全风险)
result = eval("1 + 2 * 3")         # 7(计算表达式)
# exec("print('Hello from exec')") # 执行代码块

# --- 全局/局部命名空间 ---
# globals() / locals() - 返回命名空间字典
x = 42
print(globals()['x'])              # 42

🔑 要点总结:getattr(obj, attr, default) 在不确定属性是否存在时非常安全。eval() 执行表达式、exec() 执行代码块,但需谨慎使用------执行不可信的输入有安全风险。


22. 类型注解

Python 是动态类型语言,但从 3.5+ 开始支持类型注解(Type Hints),帮助 IDE 和工具做静态检查。

22.1 基本类型注解

💡 代码说明:Python 类型注解使用 变量: 类型 语法,函数注解使用 def func(arg: Type) -> ReturnType。注解不强制类型,但能帮助 IDE 和 mypy 做静态检查:

python 复制代码
# 变量注解
name: str = "Alice"
age: int = 25
price: float = 3.99
is_active: bool = True

# 函数注解
def greet(name: str) -> str:
    return f"Hello, {name}"

def divide(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("除数不能为零")
    return a / b

# 参数默认值
def repeat(text: str, times: int = 1) -> str:
    return text * times

🔑 要点总结:类型注解是可选的,但越来越多的项目开始使用它们。结合 VS Code / PyCharm,类型注解能提供更好的代码补全和错误提示。

22.2 复杂类型注解

💡 代码说明:使用 typing 模块(或 Python 3.10+ 新语法)可以注解容器类型、可选类型、联合类型、回调函数、字面量类型和结构化字典等复杂场景:

python 复制代码
from typing import (
    List, Dict, Set, Tuple, Optional, Union,
    Callable, Any, Sequence, Iterable, Literal, TypedDict
)

# 容器类型
def process_numbers(nums: List[int]) -> Dict[int, int]:
    return {n: n ** 2 for n in nums}

def matrix_multiply(a: List[List[float]], b: List[List[float]]) -> List[List[float]]:
    # ...
    pass

# 元组(固定长度)
point: Tuple[float, float] = (3.0, 4.0)
record: Tuple[str, int, bool] = ("Alice", 25, True)

# Optional: 可以是某类型或 None
def find_user(user_id: int) -> Optional[Dict[str, Any]]:
    # 可能返回 None
    pass

# Union: 多种类型中之一(Python 3.10+ 可用 X | Y)
def parse_value(value: str) -> Union[int, float, str]:
    try:
        return int(value)
    except ValueError:
        try:
            return float(value)
        except ValueError:
            return value

# Python 3.10+ 新语法(推荐)
def parse_value_v2(value: str) -> int | float | str:
    pass

# Optional 的新写法(Python 3.10+)
def maybe_get_name() -> str | None:
    pass

# Callable: 函数类型
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

# Any: 任意类型(禁用类型检查)
def flexible(data: Any) -> Any:
    return data

# Literal: 字面量类型
def set_mode(mode: Literal["read", "write", "append"]) -> None:
    pass

set_mode("read")   # ✅
# set_mode("delete")  # ❌ 类型检查器会报错

# TypedDict: 带类型的字典
class User(TypedDict):
    name: str
    age: int
    email: str | None

def create_user(data: User) -> None:
    print(data["name"])

🔑 要点总结:Python 3.10+ 推荐使用 int | None 替代 Optionalint,int | str 替代 Unionint, str------更简洁直观。Literal 限定参数的合法值,让类型检查更精确。

22.3 自定义泛型

💡 代码说明:使用 TypeVar 创建类型变量,实现泛型函数和泛型类------让类型检查器能根据输入推断输出的具体类型:

python 复制代码
from typing import TypeVar, Generic

T = TypeVar('T')   # 类型变量

def first(items: List[T]) -> T | None:
    """返回列表第一个元素,保持类型"""
    return items[0] if items else None

a: int = first([1, 2, 3])        # 类型检查器知道返回 int
b: str = first(["a", "b", "c"])  # 类型检查器知道返回 str

# 泛型类
K = TypeVar('K')
V = TypeVar('V')

class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: List[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T | None:
        return self._items.pop() if self._items else None

int_stack = Stack[int]()
int_stack.push(1)
int_stack.push(2)
# int_stack.push("hello")  # ❌ 类型检查器会报错

🔑 要点总结:泛型在编写可复用的容器类(如 Stack、Queue)和工具函数时特别有用------既保持类型安全,又不失灵活性。


23. 虚拟环境与包管理

23.1 venv 虚拟环境

bash 复制代码
# 创建虚拟环境
python -m venv myenv

# 激活虚拟环境
# Windows:
myenv\Scripts\activate
# macOS/Linux:
# source myenv/bin/activate

# 退出虚拟环境
deactivate

23.2 pip 包管理

bash 复制代码
# 安装包
pip install requests
pip install pandas==2.0.3           # 安装特定版本
pip install "django>=4.0,<5.0"      # 版本范围

# 升级包
pip install --upgrade requests

# 卸载包
pip uninstall requests

# 列出已安装的包
pip list
pip list --outdated                 # 列出可升级的包

# 显示包详情
pip show requests

# 依赖管理
pip freeze > requirements.txt       # 导出依赖
pip install -r requirements.txt     # 安装依赖

# 搜索包
# pip search <keyword>              # 已在 PyPI 网站搜索

23.3 requirements.txt 示例

text 复制代码
# requirements.txt
requests>=2.28,<3.0
numpy==1.24.3
pandas>=2.0
flask>=2.3
python-dotenv>=1.0
black>=23.0      # 代码格式化
pylint>=2.17     # 代码检查
pytest>=7.4      # 测试框架

24. 编码规范(PEP 8)

24.1 缩进与空白

💡 代码说明:PEP 8 规定使用 4 空格缩进、每行最多 79 字符、运算符两侧加空格、函数和类之间空两行:

python 复制代码
# ✅ 使用 4 个空格缩进(不要用 Tab)
def my_function():
    if condition:
        do_something()

# ❌ 不要混用 Tab 和空格
# ❌ 不要用 2 个或 8 个空格

# 每行最多 79 个字符(文档/注释 72)
# 操作符两边各加一个空格
x = 1 + 2
y = x * 3

# 函数和类之间空两行
# 方法之间空一行

🔑 要点总结:遵循 PEP 8 让你和团队其他成员的代码风格一致。可以用 black 工具自动格式化,省去手动调整的麻烦。

24.2 命名约定

💡 代码说明:Python 社区遵循一致的命名约定:函数/变量用 snake_case,类用 PascalCase,常量全大写,私有属性前缀单下划线:

python 复制代码
# 包/模块: 简短小写,可用下划线
# my_package, utils

# 函数/方法/变量: 小写 + 下划线
def calculate_average(numbers):
    total_sum = 0
    return total_sum / len(numbers)

# 类: 大驼峰
class ShoppingCart:
    pass

# 异常: 类名 + Error
class ValidationError(Exception):
    pass

# 常量: 全大写 + 下划线
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30

# 私有属性/方法: 前缀单下划线
class MyClass:
    def __init__(self):
        self._internal_data = {}

    def _private_method(self):
        pass

# 避免名称改写: 后缀单下划线
# class_(class 是关键字)
# list_(list 是内置函数)

🔑 要点总结:一致的命名让代码意图一目了然------MAX_SIZE 一眼就是常量,_internal 表示内部使用,ValidationError 一看就是异常类。

24.3 导入顺序

💡 代码说明:import 语句应按标准库 -> 第三方库 -> 本地模块的顺序排列,每组之间空一行:

python 复制代码
# 1. 标准库
import os
import sys

# 2. 第三方库
import numpy as np
import requests

# 3. 本地模块
from .utils import helper
from mypackage.core import core_func

🔑 要点总结:isort 工具可以自动按此规则排序 import 语句,一劳永逸。

24.4 注释与文档字符串

💡 代码说明:好的注释说明为什么而不是做了什么。文档字符串用三引号,推荐 Google 风格或 NumPy 风格的格式:

python 复制代码
# 单行注释:说明代码意图(不说明做了什么,而是为什么这么做)
# 复杂正则表达式: 匹配 Email 地址 (RFC 5322)
email_pattern = r'...'

# 文档字符串: 三引号
def fetch_data(url: str, timeout: int = 10) -> dict | None:
    """
    从指定 URL 获取 JSON 数据。

    Args:
        url: API 地址
        timeout: 超时时间(秒)

    Returns:
        成功时返回解析后的字典,失败返回 None

    Raises:
        ValueError: URL 格式不正确
        requests.RequestException: 网络请求失败
    """
    pass

🔑 要点总结:Args:、Returns:、Raises: 是文档字符串的标准字段,IDE 和文档生成工具(如 Sphinx)能识别并自动生成漂亮的 API 文档。

24.5 代码格式化工具

bash 复制代码
# Black: 自动代码格式化
pip install black
black my_script.py

# isort: 自动排序 import 语句
pip install isort
isort my_script.py

# Flake8 / Pylint: 代码质量检查
pip install flake8 pylint
flake8 my_script.py
pylint my_script.py

# mypy: 静态类型检查
pip install mypy
mypy my_script.py

25. 综合练习

练习 1:学生成绩管理系统

💡 代码说明:综合运用类与对象、字典操作、列表推导式、排序等知识,实现一个完整的学生成绩管理系统。包含添加学生、记录成绩、计算平均分、排名和生成报告等功能:

python 复制代码
"""
学生成绩管理系统
功能:
1. 添加学生及其成绩
2. 计算平均分
3. 查找最高分学生
4. 按成绩排序
"""


class StudentGradeManager:
    def __init__(self):
        self.students: dict[str, list[float]] = {}

    def add_student(self, name: str) -> None:
        """添加学生"""
        if name not in self.students:
            self.students[name] = []
            print(f"已添加学生: {name}")
        else:
            print(f"学生 {name} 已存在")

    def add_grade(self, name: str, grade: float) -> None:
        """添加成绩"""
        if 0 <= grade <= 100:
            self.students.setdefault(name, [])
            self.students[name].append(grade)
            print(f"{name} 的成绩 {grade} 已记录")
        else:
            raise ValueError("成绩必须在 0-100 之间")

    def get_average(self, name: str) -> float | None:
        """获取学生平均分"""
        grades = self.students.get(name)
        return sum(grades) / len(grades) if grades else None

    def get_overall_average(self) -> float:
        """获取全班平均分"""
        all_grades = [
            grade
            for grades in self.students.values()
            for grade in grades
        ]
        return sum(all_grades) / len(all_grades) if all_grades else 0.0

    def top_student(self) -> tuple[str, float] | None:
        """获取平均分最高的学生"""
        if not self.students:
            return None
        return max(
            ((name, self.get_average(name)) for name in self.students),
            key=lambda x: x[1]
        )

    def rank_students(self) -> list[tuple[str, float]]:
        """按平均分从高到低排名"""
        ranked = [
            (name, self.get_average(name))
            for name in self.students
        ]
        return sorted(ranked, key=lambda x: x[1], reverse=True)

    def report(self) -> str:
        """生成成绩报告"""
        lines = ["=" * 40, "学生成绩报告", "=" * 40]
        for name in self.students:
            avg = self.get_average(name)
            grades = self.students[name]
            lines.append(
                f"{name}: 成绩 {grades}, 平均分 {avg:.1f}"
            )
        lines.append("-" * 40)
        lines.append(f"全班平均分: {self.get_overall_average():.1f}")
        top = self.top_student()
        if top:
            lines.append(f"最高分学生: {top[0]} ({top[1]:.1f})")
        return "\n".join(lines)


# --- 使用示例 ---
if __name__ == "__main__":
    mgr = StudentGradeManager()

    # 添加学生和成绩
    mgr.add_student("Alice")
    mgr.add_student("Bob")
    mgr.add_student("Charlie")

    mgr.add_grade("Alice", 85)
    mgr.add_grade("Alice", 92)
    mgr.add_grade("Bob", 78)
    mgr.add_grade("Bob", 83)
    mgr.add_grade("Charlie", 95)
    mgr.add_grade("Charlie", 88)

    # 打印报告
    print(mgr.report())
    print(f"\n排名: {mgr.rank_students()}")

🔑 要点总结:这个练习涵盖了 Python 基础语法的方方面面------面向对象设计、数据结构选择、异常处理、列表推导式、条件判断等。建议先理解代码结构,再动手自己实现一遍。

练习 2:装饰器练习 --- 实现 API 限流

💡 代码说明:这是一个实战级别的装饰器练习------实现 API 调用频率限制(Rate Limiting)。利用闭包保存调用历史,在超限时抛出异常:

python 复制代码
import time
from functools import wraps
from collections import defaultdict


def rate_limit(max_calls: int, period: float):
    """
    限流装饰器:在指定时间周期内限制函数调用次数

    Args:
        max_calls: 最大调用次数
        period: 时间周期(秒)
    """
    calls = defaultdict(list)  # {函数名: [调用时间列表]}

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            func_name = func.__name__

            # 清理过期的调用记录
            calls[func_name] = [
                t for t in calls[func_name]
                if now - t < period
            ]

            # 检查是否超出限制
            if len(calls[func_name]) >= max_calls:
                wait_time = period - (now - calls[func_name][0])
                raise RuntimeError(
                    f"API 限流:在 {period} 秒内最多调用 {max_calls} 次。"
                    f"请等待 {wait_time:.1f} 秒。"
                )

            # 记录调用时间
            calls[func_name].append(now)
            return func(*args, **kwargs)

        return wrapper

    return decorator


# --- 使用 ---
@rate_limit(max_calls=3, period=5)
def api_call(endpoint: str) -> str:
    """模拟 API 调用"""
    return f"API 响应: {endpoint}"


# 测试
if __name__ == "__main__":
    for i in range(5):
        try:
            result = api_call(f"/users/{i}")
            print(result)
        except RuntimeError as e:
            print(f"错误: {e}")
        time.sleep(0.5)

🔑 要点总结:这个限流装饰器展示了装饰器的实际应用价值------将横切关注点(限流)从业务逻辑(API 调用)中干净地分离出来。理解了这个例子,装饰器的核心思想就掌握了。

练习 3:生成器练习 --- 文件解析

💡 代码说明:使用生成器和正则表达式,实现一个日志文件解析器。通过 yield 逐行产出解析结果,再用管道式生成器进行过滤和统计:

python 复制代码
"""
解析日志文件并提取特定模式的数据
"""


def parse_log_file(filepath: str):
    """
    逐行读取日志文件,解析为结构化数据

    日志格式: [2026-07-21 14:30:00] [INFO] User Alice logged in
    """
    import re

    # 日志行正则
    pattern = re.compile(
        r'\[(?P<timestamp>[^\]]+)\]\s+'
        r'\[(?P<level>[A-Z]+)\]\s+'
        r'(?P<message>.+)'
    )

    with open(filepath, 'r', encoding='utf-8') as f:
        for line_num, line in enumerate(f, start=1):
            line = line.strip()
            if not line:
                continue
            match = pattern.match(line)
            if match:
                yield {
                    'line': line_num,
                    'timestamp': match.group('timestamp'),
                    'level': match.group('level'),
                    'message': match.group('message'),
                }
            else:
                yield {
                    'line': line_num,
                    'raw': line,
                    'error': '解析失败',
                }


def filter_errors(records):
    """过滤出 ERROR 级别的记录"""
    for record in records:
        if record.get('level') == 'ERROR':
            yield record


def count_by_level(records):
    """按日志级别统计"""
    from collections import Counter
    return Counter(record.get('level', 'PARSE_ERROR') for record in records)


# 创建测试日志文件
def create_test_log():
    log_content = """[2026-07-21 10:00:00] [INFO] Application started
[2026-07-21 10:00:05] [DEBUG] Loading config file
[2026-07-21 10:00:10] [INFO] Server listening on port 8080
[2026-07-21 10:01:00] [WARNING] Memory usage exceeds 80%
[2026-07-21 10:02:00] [ERROR] Connection timeout: database
[2026-07-21 10:02:30] [ERROR] Failed to send email notification
[2026-07-21 10:03:00] [INFO] Retry successful
[2026-07-21 10:05:00] [INFO] Application shutdown
malformed line without proper format
"""
    with open("test_log.txt", "w", encoding="utf-8") as f:
        f.write(log_content)


if __name__ == "__main__":
    create_test_log()

    # 解析日志
    records = list(parse_log_file("test_log.txt"))

    # 统计
    print("=== 日志统计 ===")
    counter = count_by_level(records)
    for level, count in counter.items():
        print(f"  {level}: {count} 条")

    # 显示错误
    print("\n=== 错误日志 ===")
    for error in filter_errors(records):
        print(f"  行 {error['line']}: {error['message']}")

🔑 要点总结:生成器管道模式(parse -> filter -> analyze)在处理大文件时特别有价值------每一行只被处理一次,数据流式经过各个处理环节,几乎不占内存。建议将此模式用于实际的日志分析任务中。


附录 A:Python 内置异常层级

复制代码
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
    ├── StopIteration
    ├── StopAsyncIteration
    ├── ArithmeticError
    │   ├── FloatingPointError
    │   ├── OverflowError
    │   └── ZeroDivisionError
    ├── AssertionError
    ├── AttributeError
    ├── BufferError
    ├── EOFError
    ├── ImportError
    │   └── ModuleNotFoundError
    ├── LookupError
    │   ├── IndexError
    │   └── KeyError
    ├── MemoryError
    ├── NameError
    │   └── UnboundLocalError
    ├── OSError
    │   ├── BlockingIOError
    │   ├── ChildProcessError
    │   ├── ConnectionError
    │   ├── FileExistsError
    │   ├── FileNotFoundError
    │   ├── InterruptedError
    │   ├── IsADirectoryError
    │   ├── NotADirectoryError
    │   ├── PermissionError
    │   ├── ProcessLookupError
    │   └── TimeoutError
    ├── ReferenceError
    ├── RuntimeError
    │   ├── NotImplementedError
    │   └── RecursionError
    ├── SyntaxError
    │   └── IndentationError
    │       └── TabError
    ├── SystemError
    ├── TypeError
    ├── ValueError
    │   └── UnicodeError
    └── Warning

附录 B:推荐学习资源

在线资源

资源 说明 链接
Python 官方文档 权威参考,中文版 docs.python.org/zh-cn
菜鸟教程 中文入门教程 runoob.com/python3
Real Python 英文高质量教程 realpython.com
LeetCode 编程练习 leetcode.cn
PyPI Python 包索引 pypi.org

推荐书籍

  • 《Python 编程:从入门到实践》 - Eric Matthes(入门首选)
  • 《流畅的 Python》 - Luciano Ramalho(深入理解 Python)
  • 《Python Cookbook》 - David Beazley(实战技巧)
  • 《Effective Python》 - Brett Slatkin(最佳实践)

📝 学习建议

  1. 动手实践:每学一个概念,立即写代码验证
  2. 阅读错误信息:Python 的错误信息很友好,仔细阅读
  3. 善用 REPL:交互式解释器是测试小段代码的最佳工具
  4. 阅读优秀代码:在 GitHub 上找高质量的 Python 项目学习
  5. 做项目驱动:找一个感兴趣的小项目,边做边学

本教程由 AI 辅助生成,内容覆盖 Python 3.8+ 核心语法。如有疏漏,欢迎指正!

相关推荐
爱喝水的鱼丶1 小时前
SAP-ABAP:一次关于“交货日期”的增强需求:从复杂增强到标准功能的回归之旅
运维·开发语言·性能优化·sap·abap·经验交流
白执落1 小时前
使用 Python + LangChain + Vue3 构建 LLM 聊天应用
人工智能·python·langchain
蓝创工坊Blue Foundry1 小时前
本地 PDF、图片字段提取到 Excel:用文档工作台的完整流程
python·ocr·vim·paddlepaddle
星恒随风2 小时前
C++ STL 详解:list 的使用、迭代器失效、模拟实现与 vector 对比
开发语言·数据结构·c++·笔记·学习·list
小白说大模型2 小时前
AI Agent 调试实战:链路追踪、Prompt 可视化与异常定位的系统方法
java·人工智能·python·算法·prompt
Tim_102 小时前
【C++】019、内存泄漏
java·开发语言
牢姐与蒯2 小时前
c++之异常
开发语言·c++·c++11
此生决int2 小时前
深入理解C++系列(02)——类和对象(上)
开发语言·c++
浊酒南街2 小时前
subprocess.run函数介绍
python