🐍 Python 基础语法这一篇就够了
适用版本 : Python 3.8+
目标读者 : 初学者及有一定基础想要系统复习的开发者
最后更新: 2026-07-21
目录
- [Python 简介](#Python 简介)
- 环境搭建与第一个程序
- 变量与数据类型
- 运算符
- 字符串详解
- 列表(List)
- 元组(Tuple)
- 字典(Dictionary)
- 集合(Set)
- 条件判断
- 循环语句
- 函数
- [Lambda 表达式](#Lambda 表达式)
- 列表推导式与生成器表达式
- 迭代器与生成器
- 装饰器
- 模块与包
- 文件操作
- 异常处理
- 面向对象编程
- 常用内置函数
- 类型注解
- 虚拟环境与包管理
- [编码规范(PEP 8)](#编码规范(PEP 8))
- 综合练习
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 为什么使用元组?
- 不可变性保证数据不会被意外修改
- 作为字典的键(列表不能)
- 性能略优于列表
- 语义表达:暗示这是一组不应修改的数据(如坐标、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) 至关重要------没有它,被装饰函数的 name 和 doc 会变成 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 装饰器自动生成 init 、repr 、eq 等方法,大幅减少样板代码:
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(最佳实践)
📝 学习建议
- 动手实践:每学一个概念,立即写代码验证
- 阅读错误信息:Python 的错误信息很友好,仔细阅读
- 善用 REPL:交互式解释器是测试小段代码的最佳工具
- 阅读优秀代码:在 GitHub 上找高质量的 Python 项目学习
- 做项目驱动:找一个感兴趣的小项目,边做边学
本教程由 AI 辅助生成,内容覆盖 Python 3.8+ 核心语法。如有疏漏,欢迎指正!