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

适用版本: Python 3.8+
目标读者: 初学者及有一定基础想要系统复习的开发者
最后更新: 2026-07-21


目录

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

验证安装:

python --version
# 输出示例: Python 3.11.5

2.2 第一个程序:Hello, World!

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

# hello.py
print("Hello, World!")

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

运行:

python hello.py
# 输出: Hello, World!

2.3 交互式解释器(REPL)

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

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

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

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

2.4 IDE 推荐

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

3. 变量与数据类型

3.1 变量命名规则

💡 代码说明: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 模块可以查看完整列表:

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)。下面的代码展示了每种类型的用法:

# --- 数值型 ---
# 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() 等函数在不同类型间转换:

# 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 是动态类型语言,变量可以随时改变类型
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 内存行为的关键:

# 不可变类型: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() 等内置数学函数:

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 / == 两种相等性检查:

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(而不是 &&、||、!)。逻辑运算支持短路求值,且可以作用于非布尔值:

# 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 位运算符

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

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 新增的海象运算符 :=:

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 用于检查元素是否存在于序列(列表、字符串、字典的键等)中:

# 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 运算符优先级(从高到低)

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

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

5. 字符串详解

5.1 字符串创建

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

# 四种引号方式
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、引号 " 等:

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 为步长:

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 字符串常用方法

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

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+)是最推荐的,支持表达式嵌入、格式说明符和调试模式:

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 字符串不可变性

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

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() 创建,可以包含任意类型的元素,支持乘法重复和嵌套。但要注意嵌套列表乘法的陷阱:

# 多种创建方式
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开始:

fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]

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

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

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

6.3 修改列表

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

fruits = ["苹果", "香蕉", "橙子"]

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

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

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

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

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

6.4 列表方法

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

# --- 添加元素 ---
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 深拷贝

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

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() 将多个列表打包成一对对元组,是遍历中最常用的两个内置函数:

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() 创建。特别注意:单元素元组必须在元素后加逗号,否则会被当成普通括号表达式:

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 元组的不可变性

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

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 的一大特色,可以一次性将元组(或列表)的元素赋值给多个变量。* 可以收集多余元素:

# 序列解包(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 创建轻量数据类:

# 元组作为字典的键
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() 而非直接索引:

# 创建字典
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+ 还引入了 | 和 |= 字典合并运算符:

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())。遍历时不要直接修改字典大小:

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 …} 语法快速创建或变换字典,支持条件过滤:

# {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 是字典的强力扩展,能极大简化分组和统计类代码:

# --- 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() 创建空集合,{} 创建的是空字典:

# 创建集合
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 集合操作

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

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 是不可变版本,可作为字典键:

# 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 结构,注意每个条件后必须有冒号:

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() 决定:

# 假值列表
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 更强大——支持多值匹配、序列解构、字典匹配和守卫条件:

# 类似其他语言的 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 对象等任何可迭代对象:

# 遍历列表
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 的模式:

# 基本 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 退出)时执行:

# 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() 按排序遍历——这些技巧让你的循环代码更优雅:

# 遍历两个序列(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() 查看:

# 基本函数
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(可变关键字参数)。参数顺序必须严格按照这个顺序:

# --- 位置参数 ---
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 仅限关键字参数

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

# * 之后的参数必须以关键字形式传递
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 参数解包

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

# 列表/元组解包为位置参数
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 修改外层函数的变量:

# --- 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 闭包

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

# 闭包:内部函数记住了其创建时的环境
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 是创建小型匿名函数的语法,只能包含单个表达式。

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

# 语法: 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] 语法在一行内完成映射和过滤:

# 基本语法: [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 生成器表达式

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

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

# 列表推导式:立即生成所有数据到内存
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 …} 快速创建或变换字典,是从两个列表创建映射表的绝佳方式:

# {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 …} 自动去重,本质上就是花括号包裹的列表推导式:

# {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() 逐个取值:

# 可迭代对象 (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 可以委托给子生成器:

# 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() 接收外部传入的值,实现双向通信(协程的基础):

# 生成器不仅可以产出值,还可以通过 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 中函数是对象,可以赋给变量、作为参数、作为返回值
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 保留原函数的元信息:

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 带参数的装饰器

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

# 装饰器工厂:返回装饰器的函数
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 让你能用属性语法调用方法,同时保留验证逻辑:

# --- @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 导入外部代码,支持多种导入方式和条件导入:

# --- 导入方式 ---
# 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 控制导出、导入子模块方便用户使用:

# 包是一个包含 __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 相对导入与绝对导入

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

# --- 在包内部,使用相对导入 ---
# 文件: 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 处理、正则表达式、高级集合类和迭代工具等:

# --- 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 语句确保自动关闭。支持多种读取方式(全部、分块、单行、所有行、逐行迭代)和文件模式:

# --- 写入文件 ---
# 模式: '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 的语法糖:

# 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 提供了更面向对象的路径操作方式:

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:

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 无论如何都执行:

# --- 基本结构 ---
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 … 建立异常链,保留原始错误信息:

# 自定义异常类
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(三思而后行)的预检查风格:

# ❌ 不要这样做:泛泛捕获所有异常
# 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 指向实例本身。类属性被所有实例共享,双下划线前缀触发名称改写实现私有:

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() 检查继承关系:

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 顺序,在多重继承下也能正确工作:

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 类展示了常用的魔术方法:

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 等方法,大幅减少样板代码:

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):

# 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(商和余数):

# 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(逻辑判断):

# 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 参数可以将输出重定向到文件:

# 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(动态执行):

# --- 对象相关 ---
# 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 做静态检查:

# 变量注解
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+ 新语法)可以注解容器类型、可选类型、联合类型、回调函数、字面量类型和结构化字典等复杂场景:

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 替代 Optional[int],int | str 替代 Union[int, str]——更简洁直观。Literal 限定参数的合法值,让类型检查更精确。

22.3 自定义泛型

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

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 虚拟环境

# 创建虚拟环境
python -m venv myenv

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

# 退出虚拟环境
deactivate

23.2 pip 包管理

# 安装包
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 示例

# 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 字符、运算符两侧加空格、函数和类之间空两行:

# ✅ 使用 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,常量全大写,私有属性前缀单下划线:

# 包/模块: 简短小写,可用下划线
# 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 语句应按标准库 -> 第三方库 -> 本地模块的顺序排列,每组之间空一行:

# 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 风格的格式:

# 单行注释:说明代码意图(不说明做了什么,而是为什么这么做)
# 复杂正则表达式: 匹配 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 代码格式化工具

# 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:学生成绩管理系统

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

"""
学生成绩管理系统
功能:
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)。利用闭包保存调用历史,在超限时抛出异常:

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 逐行产出解析结果,再用管道式生成器进行过滤和统计:

"""
解析日志文件并提取特定模式的数据
"""


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
PyPIPython 包索引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+ 核心语法。如有疏漏,欢迎指正!

更多推荐