Python 学习读书笔记
Python 学习读书笔记
2.4 Python break 和 continue. 9
4.6 Python 字典(Dictionary)... 25
一、Python 基础入门
1.1 Python 简介
核心内容:Python 是一种解释型、面向对象、动态类型的高级编程语言,具有简洁易读的语法(如使用缩进代替大括号),广泛应用于 Web 开发、数据分析、人工智能、自动化脚本等领域。其优势包括跨平台(Windows/macOS/Linux)、丰富的标准库和第三方库(如 NumPy、Django)、低学习门槛。
1.2 Python 入门(第一个程序)
核心内容:Python 环境搭建完成后,第一个经典程序是 "Hello World",有两种运行方式:
- 交互式环境(Python Shell):直接输入print("Hello World"),回车即执行;
- 脚本文件:创建.py文件(如hello.py),写入代码后通过python hello.py命令运行。
示例代码:
# 第一个Python程序
print("Hello World") # 输出:Hello World
print("Python学习之路") # 支持中文,无需额外配置编码(Python 3默认UTF-8)
1.3 Python 关键字和标识符
核心内容:
- 关键字:Python 内置的具有特殊含义的单词,不可作为变量名 / 函数名,如if、for、class、def等(共 35 个,可通过import keyword; print(keyword.kwlist)查看)。
- 标识符:用户自定义的变量名、函数名等,规则为:
- 由字母(a-z/A-Z)、数字(0-9)、下划线(_)组成;
- 不能以数字开头;
- 区分大小写(如name和Name是两个不同标识符);
- 避免使用关键字和内置函数名(如print、len)。
难点赘述:标识符的 "大小写区分" 和 "避免冲突" 是新手易踩坑点。
- 场景模拟:若定义print = "我的打印变量",后续调用print("Hello")会报错(因为print已被覆盖为字符串,不再是函数)。
- 规范建议:变量名用 "蛇形命名法"(如user_name),函数名同变量名,类名用 "驼峰命名法"(如UserName)。
1.4 Python 语句、缩进和注释
核心内容:
- 语句:Python 中每行代码默认是一条语句,多行语句可通过反斜杠(\)连接(如a = 1 + 2 + \ 3)。
- 缩进:Python 用缩进表示代码块(代替其他语言的{}),常用 4 个空格或 1 个 Tab(不可混用),缩进不一致会报IndentationError。
- 注释:用于解释代码,不参与执行,分为单行注释(#开头)和多行注释('''或"""包裹)。
难点赘述:缩进的语法强制性是新手高频错误点。
- 错误示例:
if 3 > 2:
print("3大于2") # 错误:if代码块未缩进
- 正确示例:
if 3 > 2:
print("3大于2") # 正确:缩进4个空格,属于if代码块
print("缩进一致才不会报错")
print("这行不属于if代码块,无论条件是否成立都会执行")
- 注释的误区:多行注释不能嵌套(如''' 外层注释 '''' 内层注释 '''会报错),需用不同符号区分(如外层''',内层#)。
1.5 Python 变量、常量和字面量
核心内容:
- 变量:用于存储数据的容器,无需声明类型,赋值即定义(如age = 20,name = "Alice"),值可修改(如age = age + 1)。
- 常量:理论上不可修改的值,Python 无严格常量语法,约定用全大写字母表示(如PI = 3.14159,但仍可修改,需靠开发者自觉)。
- 字面量:直接写在代码中的固定值,如整数(10)、浮点数(3.14)、字符串("Python")、布尔值(True/False)、None(空值)。
难点赘述:Python 变量的 "动态类型" 和 "引用传递" 特性。
- 动态类型:变量类型随赋值变化,示例:
x = 10 # x是整数类型
x = "Python" # x变为字符串类型,无需声明
- 引用传递(以列表为例):变量存储的是数据的 "引用"(地址),而非数据本身,修改可变对象(如列表)会影响所有引用它的变量:
a = [1, 2, 3]
b = a # b和a指向同一个列表(引用相同)
b.append(4)
print(a) # 输出:[1,2,3,4](a也被修改)
# 若要避免影响,需创建新对象(浅拷贝)
c = a.copy()
c.append(5)
print(a) # 输出:[1,2,3,4](a不受影响)
1.6 Python 数据类型
核心内容:Python 数据类型分 "基本类型" 和 "复合类型":
| 类型分类 | 具体类型 | 说明 | 示例 |
| 基本类型 | 整数(int) | 无大小限制的整数 | 10、-5、1000000 |
| 浮点数(float) | 带小数点的数(精度有限) | 3.14、-0.5 | |
| 布尔值(bool) | 仅True(1)或False(0) | 3 > 2结果为True | |
| 字符串(str) | 字符序列,用""或''包裹 | "Hello"、'Python' | |
| 复合类型 | 列表(list) | 有序、可变的元素集合 | [1, "a", True] |
| 元组(tuple) | 有序、不可变的元素集合 | (1, "a", True) | |
| 集合(set) | 无序、无重复的元素集合 | {1, 2, 3} | |
| 字典(dict) | 无序的键值对集合(键唯一) | {"name": "Alice"} |
难点赘述:浮点数的精度问题是新手易忽略的点。
- 问题示例:0.1 + 0.2的结果不是0.3,而是0.30000000000000004,因为浮点数在计算机中用二进制存储,部分十进制小数无法精确表示。
- 解决方案:使用decimal模块精确计算(适用于金融场景):
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2")) # 输出:0.3
1.7 Python 类型转换
核心内容:将一种数据类型转换为另一种,需使用内置转换函数,常见如下:
| 转换函数 | 说明 | 示例 |
| int(x) | 转为整数(x 需可被解析为整数) | int("10")→10,int(3.9)→3 |
| float(x) | 转为浮点数 | float("3.14")→3.14 |
| str(x) | 转为字符串 | str(10)→"10" |
| bool(x) | 转为布尔值(空 / 0 为False) | bool(0)→False,bool([])→False |
| list(x) | 转为列表(x 需为可迭代对象) | list("abc")→["a","b","c"] |
难点赘述:转换失败的异常处理和 "隐性转换" 的区别。
- 转换失败场景:若x无法被解析为目标类型,会报ValueError,如int("abc")、float("xyz")。
- 解决方案:结合异常处理(后续章节内容)捕获错误:
s = "123a"
try:
num = int(s)
except ValueError:
print(f"字符串'{s}'无法转为整数") # 输出:字符串'123a'无法转为整数
- 隐性转换:Python 在运算中自动转换类型,如10 + 3.14,会将10(int)转为10.0(float),结果为13.14(float)。
1.8 Python 输入、输出和导入
核心内容:
- 输入:input()函数,接收用户输入的内容(默认转为字符串),示例:name = input("请输入姓名:")。
- 输出:print()函数,可输出多个值(用逗号分隔,默认空格连接),支持格式化(如print(f"姓名:{name}"))。
- 导入:使用import导入模块 / 函数,常见方式:
- 导入整个模块:import math(使用时需加模块名,如math.sqrt(4));
- 导入指定函数:from math import sqrt(直接使用sqrt(4));
- 导入并改名:import math as m(用m.sqrt(4))。
难点赘述:input()的类型问题和print()的格式化技巧。
- input()的坑:无论用户输入数字还是字符串,input()均返回字符串,需手动转换类型:
age = input("请输入年龄:") # 用户输入20,age是"20"(字符串)
age = int(age) # 转为整数后才能参与运算
print(f"明年年龄:{age + 1}") # 正确:输出"明年年龄:21"
- print()格式化的 3 种方式(推荐 f-string,Python 3.6 + 支持):
name = "Alice"
age = 20
# 方式1:%格式化
print("姓名:%s,年龄:%d" % (name, age))
# 方式2:format()方法
print("姓名:{},年龄:{}".format(name, age))
# 方式3:f-string(最简洁)
print(f"姓名:{name},年龄:{age}")
1.9 Python 运算符
核心内容:按功能分为算术运算符、赋值运算符、比较运算符、逻辑运算符、成员运算符等,常用如下:
| 运算符类型 | 具体运算符 | 说明 | 示例 |
| 算术运算符 | +、-、*、/ | 加、减、乘、除(/返回 float) | 5 / 2→2.5 |
| // | 整除(取整) | 5 // 2→2 | |
| % | 取余 | 5 % 2→1 | |
| ** | 幂运算 | 2 ** 3→8 | |
| 比较运算符 | ==、!=、>、< | 等于、不等于、大于、小于 | 3 == 3→True |
| 逻辑运算符 | and、or、not | 与、或、非(短路求值) | 3>2 and 1<0→False |
| 成员运算符 | in、not in | 判断元素是否在序列中 | "a" in "abc"→True |
难点赘述:整除的正负号问题和逻辑运算符的 "短路求值"。
- 整除//的规则:结果向负无穷方向取整,而非四舍五入:
| print(5 // 2) # 输出:2(5/2=2.5,向负无穷取整为2) print(-5 // 2) # 输出:-3(-5/2=-2.5,向负无穷取整为-3) |
- 短路求值:逻辑运算中,若前半部分已能确定结果,后半部分不再执行(提高效率):
- and:前半为False,后半不执行(如False and print("不会执行"),print不触发);
- or:前半为True,后半不执行(如True or print("不会执行"),print不触发)。
1.10 Python 关键字列表
核心内容:Python 3.12 版本共 35 个关键字,核心分类如下:
- 控制流:if、elif、else、for、while、break、continue、pass;
- 函数 / 类:def、class、return、yield、lambda、global、nonlocal;
- 数据类型:True、False、None、int、float、str、list、tuple、set、dict;
- 其他:import、from、as、try、except、finally、raise、with、in、not、and、or、is、del。
使用建议:通过keyword模块实时查看当前版本关键字,避免误用:
import keyword
print("当前Python关键字:", keyword.kwlist)
print("'for'是否为关键字?", keyword.iskeyword("for")) # 输出:True
二、Python 流程控制
2.1 Python if...else 语句
核心内容:根据条件判断执行不同代码块,语法如下:
- 基础 if:if 条件: 代码块(条件为True时执行);
- if-else:if 条件: 代码块1 else: 代码块2(二选一执行);
- if-elif-else:if 条件1: 代码块1 elif 条件2: 代码块2 else: 代码块3(多条件判断,仅执行第一个满足条件的代码块)。
难点赘述:条件表达式的简写和 "真值判断"。
- 条件表达式(三目运算符):简化if-else,语法值1 if 条件 else 值2,示例:
age = 20
status = "成年" if age >= 18 else "未成年"
print(status) # 输出:成年
- 真值判断:条件无需显式写==,空值 / 0 会被视为False,非空 / 非 0 视为True,示例:
name = "Alice"
if name: # 等价于if name != ""
print(f"姓名:{name}") # 执行
num = 0
if num: # 等价于if num != 0
print("num非0") # 不执行
2.2 Python for 循环
核心内容:用于遍历可迭代对象(如列表、字符串、字典、range等),语法for 变量 in 可迭代对象: 代码块,常见场景:
- 遍历列表:for item in [1,2,3]: print(item);
- 遍历字符串:for char in "Python": print(char);
- 遍历字典:需指定遍历keys(默认)、values或items:
info = {"name": "Alice", "age": 20}
# 遍历键
for key in info:
print(key) # 输出:name, age
# 遍历值
for value in info.values():
print(value) # 输出:Alice, 20
# 遍历键值对(用元组接收)
for key, value in info.items():
print(f"{key}: {value}") # 输出:name: Alice, age: 20
- range()函数:生成整数序列,语法range(开始, 结束, 步长)(左闭右开),示例for i in range(1, 5): print(i)(输出 1-4)。
难点赘述:遍历中修改列表的陷阱和range的惰性特性。
- 遍历中修改列表的坑:直接删除 / 添加元素会导致索引混乱,示例:
# 错误:删除列表中的偶数,结果不完整
nums = [1,2,3,4,5]
for num in nums:
if num % 2 == 0:
nums.remove(num)
print(nums) # 输出:[1,3,5]?实际输出[1,3,5](本例碰巧正确,下例错误)
# 错误示例:删除[1,2,3,4,5,6]中的偶数
nums = [1,2,3,4,5,6]
for num in nums:
if num % 2 == 0:
nums.remove(num)
print(nums) # 输出:[1,3,5]?实际输出[1,3,5](再试[2,4,6])
nums = [2,4,6]
for num in nums:
if num % 2 == 0:
nums.remove(num)
print(nums) # 输出:[4](因为删除2后,列表变为[4,6],下一个遍历元素是6,删除后列表变为[4],循环结束)
-
- 解决方案:遍历列表的副本(如for num in nums.copy():),或倒序遍历:
nums = [2,4,6]
for num in nums.copy(): # 遍历副本
if num % 2 == 0:
nums.remove(num)
print(nums) # 输出:[](正确)
- range的惰性特性:range(1, 1000000)不直接生成百万个数字,而是在遍历中动态生成,节省内存。
2.3 Python while 循环
核心内容:根据条件重复执行代码块,语法while 条件: 代码块,需确保条件最终为False(否则无限循环),常见场景:
- 固定次数循环(用计数器):
count = 0
while count < 3:
print(f"第{count+1}次循环")
count += 1 # 计数器更新,避免无限循环
- 无限循环(需内部用break终止):
while True:
user_input = input("请输入'退出'结束:")
if user_input == "退出":
break # 终止循环
print(f"你输入的是:{user_input}")
难点赘述:无限循环的排查和while-else的用法。
- 无限循环原因:计数器未更新、条件恒为True(如while 1:),排查方法:打印计数器值,确认是否更新:
count = 0
while count < 3:
print(f"count: {count}")
# 忘记写count += 1,导致count始终为0,无限循环
- while-else:循环正常结束(未被break终止)时,执行else代码块,示例:
count = 0
while count < 3:
print(f"循环中,count={count}")
count += 1
else:
print("循环正常结束") # 执行(count达到3,条件不满足)
count = 0
while count < 3:
print(f"循环中,count={count}")
if count == 1:
break # 提前终止循环
count += 1
else:
print("循环正常结束") # 不执行(被break终止)
2.4 Python break 和 continue
核心内容:
- break:终止当前所在的循环(跳出整个循环),不执行后续迭代;
- continue:跳过当前迭代的剩余代码,直接进入下一次迭代。
难点赘述:嵌套循环中break的作用范围和continue的 "跳过" 逻辑。
- 嵌套循环中break:仅终止内层循环,外层循环继续:
# 外层循环3次,内层循环3次,内层遇到2则break
for i in range(1, 4):
print(f"外层循环:i={i}")
for j in range(1, 4):
if j == 2:
break # 仅终止内层循环
print(f" 内层循环:j={j}")
# 输出:
# 外层循环:i=1
# 内层循环:j=1
# 外层循环:i=2
# 内层循环:j=1
# 外层循环:i=3
# 内层循环:j=1
- continue的示例:遍历 1-5,跳过偶数:
for num in range(1, 6):
if num % 2 == 0:
continue # 跳过当前迭代的print(num)
print(num) # 仅输出奇数:1,3,5
2.5 Python pass 语句
核心内容:pass是 "空语句",无实际功能,仅用于占位(避免语法错误),常见场景:
- 定义空函数 / 类(后续补充代码):
def my_function():
pass # 暂时不写逻辑,避免报错
class MyClass:
pass # 暂时不写属性/方法
- 空代码块(如if、for中):
age = 17
if age < 18:
pass # 暂时不处理未成年逻辑,先占位
else:
print("成年")
难点赘述:pass与注释的区别:
- 注释:# 这是注释,解释代码,Python 解释器会忽略;
- pass:是语法层面的 "空操作",解释器会执行(但无任何效果),若不写pass,空代码块会报语法错误:
def my_func():
# 这是注释,没有pass会报错
# 报错:IndentationError: expected an indented block
三、Python 函数
3.1 Python 函数定义与调用
核心内容:函数是可复用的代码块,用def定义,语法:
def 函数名(参数):
"""函数文档字符串(说明功能)"""
函数体
return 返回值 # 可选,无return则返回None
示例:定义计算两数之和的函数:
def add(a, b):
"""计算两个数的和"""
result = a + b
return result
# 调用函数
sum_result = add(3, 5)
print(sum_result) # 输出:8
print(add.__doc__) # 输出:计算两个数的和(查看文档字符串)
难点赘述:函数的 "参数默认值陷阱"(默认参数为可变对象时):
- 错误示例:默认参数是列表,多次调用函数会累积值:
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # 输出:[1]
print(add_item(2)) # 输出:[1,2](预期是[2],实际累积)
- 原因:默认参数在函数定义时初始化一次,而非每次调用时初始化;
- 解决方案:默认参数用None,在函数内初始化可变对象:
def add_item(item, items=None):
if items is None:
items = [] # 每次调用时重新创建列表
items.append(item)
return items
print(add_item(1)) # 输出:[1]
print(add_item(2)) # 输出:[2](正确)
3.2 Python 函数参数类型
核心内容:Python 函数参数分为以下几类:
- 位置参数:必须按顺序传递,数量必须匹配函数定义。
- 默认参数:在定义时为参数提供默认值,调用时可以省略。但需要注意默认参数的位置,非默认参数不能跟随默认参数。
- 关键字参数:调用时通过参数名指定值,允许改变参数的顺序,但必须放在位置参数之后。
- 可变参数:
- *args:接收任意数量的位置参数,打包成元组;
- **kwargs:接收任意数量的关键字参数,打包成字典。
难点赘述:参数混合使用的规则和可变参数的解包。
- 参数混合使用的顺序:位置参数 → 可变位置参数(*args)→ 关键字参数 → 可变关键字参数(**kwargs)。
def func(a, *args, b=10, **kwargs):
print(a, args, b, kwargs)
func(1, 2, 3, b=20, c=30) # 输出:1 (2, 3) 20 {'c': 30}
- 可变参数的解包:
# 位置参数解包
def greet(a, b, c):
print(f"Hello {a}, {b}, {c}")
args = (1, 2, 3)
greet(*args) # 输出:Hello 1, 2, 3
# 关键字参数解包
kwargs = {"a": "Alice", "b": "Bob", "c": "Charlie"}
greet(**kwargs) # 输出:Hello Alice, Bob, Charlie
3.3 Python 递归 (Recursion)
核心内容:递归是函数直接或间接调用自身的过程,必须包含终止条件。常见示例:
- 计算阶乘:
def factorial(n):
if n == 0:
return 1 # 终止条件
return n * factorial(n-1) # 递归调用
print(factorial(5)) # 输出:120
- 斐波那契数列:
def fibonacci(n):
if n <= 1:
return n # 终止条件
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(6)) # 输出:8(斐波那契数列前7项:0,1,1,2,3,5,8)
难点赘述:递归深度限制和性能问题。
- 递归深度限制:Python 默认递归深度有限制(约 1000 层),超过会报RecursionError。
# 测试递归深度限制
def deep_recursion(n):
if n == 0:
return
deep_recursion(n+1)
# deep_recursion(0) # 会报RecursionError: maximum recursion depth exceeded
- 解决方案:改用迭代方式或增加递归深度限制(不推荐):
import sys
sys.setrecursionlimit(1000000) # 增加递归深度限制(不推荐)
- 性能问题:递归可能导致重复计算(如斐波那契数列的递归实现),可用缓存优化(装饰器lru_cache):
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(100)) # 高效计算第100项
3.4 Python 匿名函数 (Lambda)
核心内容:匿名函数(Lambda 函数)是没有函数名的小型函数,用lambda关键字定义,语法:
lambda 参数: 表达式;示例:
# 普通函数
def add(a, b):
return a + b
# 等价的Lambda函数
add_lambda = lambda a, b: a + b
print(add_lambda(3, 5)) # 输出:8
难点赘述:Lambda 函数的适用场景和限制。(map和filter对象只能被消费一次,第二次调用就会变为空)
- 适用场景:
- 作为其他函数的参数(如sorted、map、filter):
# 按年龄排序
people = [("Alice", 25), ("Bob", 30), ("Charlie", 20)]
people_sorted = sorted(people, key=lambda x: x[1])
print(people_sorted) # 输出:[("Charlie", 20), ("Alice", 25), ("Bob", 30)]
# 获取列表中的偶数和值为1的数据
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
even_numbers = filter(lambda x: x % 2 == 0 or x == 1, numbers)
print(list(even_numbers)) # 输出:[1, 2, 4, 6, 8]
print(list(even_numbers)) # 输出:[]
-
- 简单函数的临时使用:
# 计算平方
squares = map(lambda x: x**2, [1, 2, 3, 4, 5])
print(list(squares)) # 输出:[1, 4, 9, 16, 25]
print(list(squares)) # 输出:[]
- 限制:
- Lambda 函数只能有一个表达式,不能包含多条语句或return语句;
- 可读性较差,复杂逻辑不建议使用 Lambda 函数。
3.5 Python 全局、局部和非局部变量
核心内容:
- 全局变量:在函数外部定义的变量,作用域为整个模块;
- 局部变量:在函数内部定义的变量,作用域为函数内部;
- 非局部变量:在嵌套函数中,内部函数引用外部函数的变量。
难点赘述:变量作用域的 LEGB 规则和修改全局 / 非局部变量的方法。
- LEGB 规则:Python 查找变量时遵循的顺序:Local → Enclosing → Global → Built-in。
x = "全局变量"
def outer():
x = "外部函数变量"
def inner():
x = "内部函数变量"
print("内部函数:", x) # 输出:内部函数:内部函数变量
inner()
print("外部函数:", x) # 输出:外部函数:外部函数变量
outer()
print("全局:", x) # 输出:全局:全局变量
- 修改全局变量:使用global关键字声明。
count = 0
def increment():
global count # 声明使用全局变量count
count += 1
print(count)
increment() # 输出:1
increment() # 输出:2
- 修改非局部变量:使用nonlocal关键字声明。
def outer():
x = 10
def inner():
nonlocal x # 声明使用外部函数的变量x
x += 5
print(x) # 输出:15
inner()
print(x) # 输出:15
outer()
3.6 Python Global 关键字
核心内容:global关键字用于在函数内部声明使用全局变量,使函数可以修改全局变量的值。
难点赘述:global与nonlocal的区别及使用场景。
- global用于声明全局变量,nonlocal用于声明外部函数的变量。
# global示例
x = 10
def func():
global x
x = 20
print(x) # 输出:20
func()
print(x) # 输出:20(全局变量x被修改)
# nonlocal示例
def outer():
x = 10
def inner():
nonlocal x
x = 20
print(x) # 输出:20
inner()
print(x) # 输出:20(外部函数的x被修改)
outer()
- 使用场景:
- global:在函数内部修改全局变量;
- nonlocal:在嵌套函数内部修改外部函数的变量;
- 避免滥用global和nonlocal,优先通过参数传递和返回值来实现数据交换。
3.7 Python 模块
核心内容:模块是包含 Python 代码的文件(.py文件),用于组织代码,提高代码复用性。
- 导入模块:
import module_name # 导入整个模块
from module_name import function_name # 导入特定函数
from module_name import * # 导入模块中的所有函数(不推荐)
import module_name as mn # 导入并命名
- 模块的__name__属性:每个模块都有__name__属性,当模块作为主程序运行时,__name__为"__main__";当模块被导入时,__name__为模块名。
难点赘述:模块的搜索路径和__init__.py文件的作用。
- 模块搜索路径:Python 解释器查找模块的路径列表,可通过sys.path查看和修改。
import sys
print(sys.path) # 输出模块搜索路径列表
sys.path.append("/path/to/module") # 添加自定义路径
- __init__.py文件:在 Python 3.3 之前,目录中包含__init__.py文件才被视为包。__init__.py可以为空,也可以包含包的初始化代码。
| mypackage/ __init__.py module1.py module2.py |
3.8 Python 包(Package)
核心内容:包是组织多个模块的目录结构,用于管理大型项目。包的结构通常如下:
| mypackage/ __init__.py module1.py module2.py subpackage/ __init__.py submodule.py |
难点赘述:相对导入和绝对导入的区别。
- 绝对导入:从包的根目录开始导入。
# 在submodule.py中导入module1
from mypackage import module1
- 相对导入:基于当前模块的相对位置导入。
# 在submodule.py中导入module1
from .. import module1 # 相对导入父包中的module1
- 注意事项:
- 相对导入只能在包内部使用;
- 主模块(__main__)不能使用相对导入;
- Python 3 推荐使用绝对导入,提高代码可读性和可维护性。
3.9 Python 自定义函数
核心内容:用户可以根据需求定义自己的函数,实现特定功能。自定义函数的最佳实践包括:
- 函数命名:使用 "蛇形命名法"(如my_function);
- 参数设计:明确参数的类型和用途;
- 返回值:明确函数的返回值类型和含义;
- 文档字符串:为函数编写清晰的文档字符串,说明功能、参数、返回值等。
难点赘述:函数参数的设计原则和异常处理。
- 参数设计原则:
- 参数数量不宜过多,尽量控制在 5 个以内;
- 使用默认参数提供合理的默认值;
- 避免使用可变对象作为默认参数;
- 考虑参数的顺序(位置参数在前,默认参数在后)。
- 异常处理:在函数内部处理可能的异常,提高函数的健壮性。
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
print("错误:除数不能为零")
return None
print(divide(10, 2)) # 输出:5.0
print(divide(10, 0)) # 输出:错误:除数不能为零,返回None
- 函数式编程与过程式编程的选择:根据问题特性选择合适的编程范式。
- 函数式编程:强调纯函数(无副作用,相同输入总是返回相同输出);
- 过程式编程:允许有副作用(如修改全局变量)。
四、Python 数据类型
4.1 Python 数字 (Number)
核心内容:Python 支持三种数字类型:
- 整数(int):表示整数,大小不限(受限于内存);
- 浮点数(float):表示带小数点的数,精度有限;
- 复数(complex):表示复数,形式为a + bj。
难点赘述:浮点数的精度问题和数值运算的类型转换。
- 浮点数精度问题:
- 示例:0.1 + 0.2的结果不是0.3,而是0.30000000000000004。
- 原因:浮点数在计算机中以二进制表示,部分十进制小数无法精确表示。
- 解决方案:使用decimal模块进行高精度计算。
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2")) # 输出:0.3
- 数值运算的类型转换:
- 自动转换:当不同类型的数值进行运算时,Python 会自动将整数转换为浮点数。
print(10 + 3.14) # 输出:13.14(10被转为10.0)
-
- 强制转换:使用int()、float()、complex()进行类型转换。
print(int(3.9)) # 输出:3(向下取整)
print(float(5)) # 输出:5.0
print(complex(2)) # 输出:(2+0j)
4.2 Python 列表(List)
核心内容:列表是有序、可变的元素集合,用[]表示,元素类型可以不同。
难点赘述:列表的索引、切片和常见操作。
- 索引和切片:
fruits = ["apple", "banana", "cherry"]
print(fruits[0]) # 输出:apple(索引从0开始)
print(fruits[-1]) # 输出:cherry(负索引从末尾开始)
print(fruits[1:3]) # 输出:["banana", "cherry"](切片包括起始索引,不包括结束索引)
- 常见操作:
fruits = ["apple", "banana", "cherry"]
fruits.append("date") # 添加到末尾
fruits.insert(1, "blueberry") # 在指定位置插入
print(fruits) # 输出:["apple", "blueberry", "banana", "cherry", "date"]
fruits.remove("banana") # 按值删除
print(fruits) # 输出:["apple", "blueberry", "cherry", "date"]
popped = fruits.pop(1) # 按索引删除并返回删除的元素
print(popped) # 输出:blueberry
del fruits[0] # 按索引删除
print(fruits) # 输出:["cherry", "date"]
print("cherry" in fruits) # 输出:True(检查元素是否存在)
print(fruits.index("date")) # 输出:1(查找元素索引)
fruits.sort() # 原地排序
print(fruits) # 输出:["cherry", "date"](按字母顺序)
-
- 添加元素:append()、insert();
- 删除元素:pop()、remove()、del;
- 查找元素:in运算符、index()方法;
- 排序:sort()(原地排序)、sorted()(返回新列表)。
- 列表推导式:简洁创建列表的方式。
squares = [x**2 for x in range(1, 6)] # 生成1到5的平方列表
print(squares) # 输出:[1, 4, 9, 16, 25]
even_numbers = [x for x in range(1, 11) if x % 2 == 0] # 筛选偶数
print(even_numbers) # 输出:[2, 4, 6, 8, 10]
4.3 Python 元组(Tuple)
核心内容:元组是有序、不可变的元素集合,用()表示,元素类型可以不同。
难点赘述:元组与列表的区别和元组的不可变性。
- 元组与列表的区别:
- 元组不可变,列表可变;
- 元组使用(),列表使用[];
- 元组的创建和访问速度比列表快;
- 元组适用于存储不可变的数据,列表适用于需要频繁修改的数据。
- 元组的不可变性:元组的元素一旦创建就不能修改,但如果元素是可变对象(如列表),则可以修改其内容。
# 元组示例
person = ("Alice", 30, ["Python", "Java"])
print(person[0]) # 输出:Alice
# person[1] = 31 # 错误:元组元素不可变
# 修改元组中的列表元素
person[2].append("C++")
print(person) # 输出:("Alice", 30, ["Python", "Java", "C++"])
- 元组的解包:将元组的元素赋值给多个变量。
point = (3, 5)
x, y = point # 元组解包
print(x, y) # 输出:3 5
# 交换两个变量的值
a = 10
b = 20
a, b = b, a # 元组解包实现交换
print(a, b) # 输出:20 10
4.4 Python 字符串(String)
核心内容:字符串是不可变的字符序列,用""或''表示。
难点赘述:字符串的操作、格式化和编码问题。
- 字符串操作:
s = "Hello, World!"
print(s[0]) # 输出:H
print(s[7:]) # 输出:World!
print(s + " Welcome!") # 输出:Hello, World! Welcome!
print("-" * 20) # 输出:--------------------
print(s.find("World")) # 输出:7(找到返回起始索引)
print(s.find("Python")) # 输出:-1(未找到返回-1)
print(s.replace("World", "Python")) # 输出:Hello, Python!
parts = s.split(", ")
print(parts) # 输出:["Hello", "World!"]
print(" ".join(parts)) # 输出:Hello World!
-
- 索引和切片:与列表类似;
- 连接和重复:+和*运算符;
- 查找和替换:find()、replace();
- 分割和合并:split()、join()。
- 字符串格式化:
- 旧式格式化(% 操作符):
name = "Alice"
age = 30
print("姓名:%s,年龄:%d" % (name, age)) # 输出:姓名:Alice,年龄:30
-
- 新式格式化(format () 方法):
print("姓名:{},年龄:{}".format(name, age)) # 输出:姓名:Alice,年龄:30
print("姓名:{0},年龄:{1},{0}今年{1}岁".format(name, age)) # 输出:姓名:Alice,年龄:30,Alice今年30岁
-
- f-string(推荐):
print(f"姓名:{name},年龄:{age}") # 输出:姓名:Alice,年龄:30
- 字符串编码:
- Python 3 默认使用 UTF-8 编码;
- 使用encode()方法将字符串转换为字节(bytes),decode()方法将字节转换为字符串。
s = "你好,世界"
b = s.encode("utf-8") # 转换为UTF-8字节
print(b) # 输出:b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c'
s2 = b.decode("utf-8") # 转换回字符串
print(s2) # 输出:你好,世界
4.5 Python 集合(Set)
核心内容:集合是无序、无重复元素的集合,用{}或set()创建。
难点赘述:集合的操作和用途。
- 集合的创建:
s = {1, 2, 3, 4}
print(s) # 输出:{1, 2, 3, 4}
# 空集合必须用set()创建
empty_set = set()
print(empty_set) # 输出:set()
- 集合操作:
s = {1, 2, 3}
s.add(4)
print(s) # 输出:{1, 2, 3, 4}
s.update({5, 6})
print(s) # 输出:{1, 2, 3, 4, 5, 6}
s.remove(3)
print(s) # 输出:{1, 2, 4, 5, 6}
s.discard(7) # 不报错
s.discard(5)
print(s) # 输出:{1, 2, 4, 6}
# 集合运算
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b) # 并集:{1, 2, 3, 4, 5, 6}
print(a & b) # 交集:{3, 4}
print(a - b) # 差集:{1, 2}
print(a ^ b) # 对称差:{1, 2, 5, 6}
-
- 添加元素:add()、update();
- 删除元素:remove()(不存在则报错)、discard()(不存在不报错);
- 集合运算:并集(|)、交集(&)、差集(-)、对称差(^)。
- 集合的用途:
# 列表去重
lst = [1, 2, 2, 3, 3, 3]
unique = list(set(lst))
print(unique) # 输出:[1, 2, 3](注意:集合是无序的)
-
- 去重:快速去除列表中的重复元素;
- 成员检测:in操作的时间复杂度为 O (1),比列表快;
- 数学集合运算:方便进行并、交、差等操作。
4.6 Python 字典(Dictionary)
核心内容:字典是无序的键值对集合,用{}表示,键必须是不可变类型(如字符串、数字、元组)。
难点赘述:字典的操作、遍历和性能。
- 字典操作:
person = {"name": "Alice", "age": 30, "hobbies": ["reading", "travel"]}
print(person["name"]) # 输出:Alice
print(person.get("age")) # 输出:30
print(person.get("gender", "未知")) # 输出:未知(指定默认值)
person["age"] = 31 # 修改现有键的值
person["gender"] = "female" # 添加新键值对
print(person) # 输出:{"name": "Alice", "age": 31, "hobbies": ["reading", "travel"], "gender": "female"}
del person["gender"] # 删除键值对
print(person) # 输出:{"name": "Alice", "age": 31, "hobbies": ["reading", "travel"]}
popped = person.pop("age")
print(popped) # 输出:31
print(person) # 输出:{"name": "Alice", "hobbies": ["reading", "travel"]}
print("name" in person) # 输出:True
print("gender" in person) # 输出:False
print(person.keys()) # 输出:dict_keys(["name", "hobbies"])
print(person.values()) # 输出:dict_values(["Alice", ["reading", "travel"]])
print(person.items()) # 输出:dict_items([("name", "Alice"), ("hobbies", ["reading", "travel"])])
-
- 访问和修改:[]或get();
- 添加和删除:直接赋值、del、pop();
- 键的存在性检查:in运算符;
- 获取键、值、项:keys()、values()、items()。
- 字典遍历:
person = {"name": "Alice", "age": 30}
# 遍历键
for key in person:
print(key) # 输出:name, age
# 遍历值
for value in person.values():
print(value) # 输出:Alice, 30
# 遍历键值对
for key, value in person.items():
print(f"{key}: {value}") # 输出:name: Alice, age: 30
- 字典推导式:
squares = {x: x**2 for x in range(1, 6)}
print(squares) # 输出:{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
even_squares = {x: x**2 for x in range(1, 6) if x % 2 == 0}
print(even_squares) # 输出:{2: 4, 4: 16}
- 字典的性能:
- 字典的查找和插入操作时间复杂度为 O (1)(平均情况下);
- 字典的内存开销较大,适合需要频繁查找和修改的数据。
4.7 Python 集合方法
核心内容:集合对象提供了一系列方法,用于集合的操作和运算。
难点赘述:集合方法的使用场景和性能比较。
- 集合方法分类:
s1 = {1, 2, 3}
s2 = {3, 4, 5}
# 集合运算方法
print(s1.union(s2)) # 并集:{1, 2, 3, 4, 5}
print(s1.intersection(s2)) # 交集:{3}
print(s1.difference(s2)) # 差集:{1, 2}
print(s1.symmetric_difference(s2)) # 对称差:{1, 2, 4, 5}
# 集合关系检查
print(s1.issubset({1, 2, 3, 4})) # 输出:True
print({1, 2}.issuperset(s1)) # 输出:False
# 复制集合
s3 = s1.copy()
print(s3) # 输出:{1, 2, 3}
-
- 基础方法:add()、remove()、discard()、clear();
- 集合运算方法:union()、intersection()、difference()、symmetric_difference();
- 其他方法:copy()、issubset()、issuperset()。
- 集合方法与运算符的区别:
s1 = {1, 2}
s2 = {2, 3}
s3 = {3, 4}
print(s1.union(s2, s3)) # 输出:{1, 2, 3, 4}
-
- 运算符(如|、&、-、^)返回新集合;
- 方法(如union()、intersection()等)可以接受多个集合作为参数。
- 集合方法的性能:
- 集合的成员检测(in)时间复杂度为 O (1);
- 集合的并、交、差运算时间复杂度与集合的大小有关;
- 避免在循环中频繁进行集合运算,尤其是大集合。
五、Python 文件操作
5.1 Python 文件 I/O
核心内容:Python 提供了open()函数用于文件操作,支持多种模式(读、写、追加等)。
难点赘述:文件打开模式、文件对象的方法和上下文管理器。
- 文件打开模式:
# 打开文件(默认文本模式)
f = open("example.txt", "r")
# 二进制模式打开图片文件
with open("image.jpg", "rb") as f:
data = f.read()
-
- r:只读模式(默认),文件不存在则报错;
- w:写入模式,文件存在则覆盖,不存在则创建;
- a:追加模式,文件存在则在末尾追加,不存在则创建;
- r+:读写模式;
- b:二进制模式(如rb、wb);
- t:文本模式(默认,如rt、wt)。
- 文件对象的方法:
# 读取整个文件内容
with open("example.txt", "r") as f:
content = f.read()
print(content)
# 逐行读取
with open("example.txt", "r") as f:
line = f.readline()
while line:
print(line.strip()) # 去除换行符
line = f.readline()
# 读取所有行到列表
with open("example.txt", "r") as f:
lines = f.readlines()
print(lines)
# 写入文件
with open("output.txt", "w") as f:
f.write("Hello, World!\n")
f.write("This is a test.\n")
# 写入多行
lines = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open("output.txt", "a") as f:
f.writelines(lines)
# 文件定位
with open("example.txt", "r") as f:
print(f.tell()) # 输出:0(当前位置)
f.seek(5) # 移动到第5个字节
print(f.tell()) # 输出:5
print(f.read(1)) # 读取一个字符
-
- 读取:read()、readline()、readlines();
- 写入:write()、writelines();
- 定位:seek()、tell()。
- 使用上下文管理器(with语句):
# 不使用with语句
f = open("example.txt", "r")
content = f.read()
f.close() # 必须手动关闭
# 使用with语句
with open("example.txt", "r") as f:
content = f.read()
# 离开with块后,文件自动关闭
-
- 自动管理文件的打开和关闭,确保文件资源被正确释放;
- 避免忘记关闭文件导致的资源泄漏。
5.2 Python 目录和文件管理
核心内容:Python 提供了os和shutil模块用于目录和文件的管理。
难点赘述:目录操作、文件路径处理和跨平台兼容性。
- 目录操作:
import os
# 创建目录(单级)
os.mkdir("test_dir")
# 创建多级目录
os.makedirs("test_dir/sub_dir")
# 删除空目录
os.rmdir("test_dir/sub_dir")
# 删除非空目录(需要shutil模块)
import shutil
shutil.rmtree("test_dir")
# 切换目录
os.chdir("/path/to/directory")
# 获取当前目录
print(os.getcwd())
# 列出目录内容
print(os.listdir("."))
-
- 创建目录:os.mkdir()、os.makedirs();
- 删除目录:os.rmdir()、shutil.rmtree();
- 切换目录:os.chdir();
- 获取当前目录:os.getcwd();
- 列出目录内容:os.listdir()。
- 文件路径处理:
import os
# 路径拼接
path = os.path.join("dir1", "dir2", "file.txt")
print(path) # 输出:dir1/dir2/file.txt(Linux/macOS)或 dir1\dir2\file.txt(Windows)
# 分离路径和文件名
dirname, filename = os.path.split(path)
print(dirname) # 输出:dir1/dir2
print(filename) # 输出:file.txt
# 获取文件扩展名
print(os.path.splitext(filename)) # 输出:("file", ".txt")
# 路径检查
print(os.path.exists(path)) # 检查路径是否存在
print(os.path.isfile(path)) # 检查是否为文件
print(os.path.isdir(path)) # 检查是否为目录
-
- 拼接路径:os.path.join();
- 分离路径和文件名:os.path.split();
- 获取文件名和扩展名:os.path.basename()、os.path.splitext();
- 判断路径是否存在:os.path.exists();
- 判断是否为文件或目录:os.path.isfile()、os.path.isdir()。
- 跨平台兼容性:
# 错误示例(硬编码路径分隔符)
path = "C:\data\file.txt" # 在Linux/macOS上会出错
# 正确示例
path = os.path.join("C:", "data", "file.txt")
-
- 使用os模块的函数处理路径,避免直接使用操作系统特定的路径分隔符;
- 使用os.path模块的函数进行路径操作,确保代码在不同平台上的兼容性。
5.3 Python 错误和内置异常
核心内容:Python 中的错误分为语法错误(SyntaxError)和异常(Exception),异常可以被捕获和处理。
难点赘述:异常的分类、自定义异常和异常处理的最佳实践。
- 异常分类:
# 常见内置异常
try:
x = 10 / 0
except ZeroDivisionError as e:
print("除数不能为零:", e)
try:
print(my_list[100])
except IndexError as e:
print("索引错误:", e)
try:
int("abc")
except ValueError as e:
print("值错误:", e)
-
- 内置异常:Python 预定义的异常,如ValueError、TypeError、IndexError等;
- 用户自定义异常:继承自Exception类的异常。
- 异常处理结构:
# try-except-else-finally结构
try:
x = int(input("请输入一个整数:"))
result = 10 / x
except ValueError:
print("错误:输入不是整数")
except ZeroDivisionError:
print("错误:除数不能为零")
else:
print("结果:", result)
finally:
print("执行结束")
-
- try-except:捕获特定异常;
- try-except-else:无异常时执行else块;
- try-except-finally:无论是否发生异常,都执行finally块;
- try-except-except-...:捕获多个不同的异常。
- 自定义异常:
class MyError(Exception):
"""自定义异常类"""
def __init__(self, message):
super().__init__(message)
self.message = message
# 使用自定义异常
try:
raise MyError("这是一个自定义异常")
except MyError as e:
print(e.message) # 输出:这是一个自定义异常
- 异常处理最佳实践:
- 捕获具体的异常,而非通用的Exception或BaseException;
- 在适当的层次处理异常;
- 避免在循环中处理异常,应优先使用条件判断;
- 在异常处理中记录日志,而非简单打印。
5.4 Python 异常处理
核心内容:Python 的异常处理机制允许程序捕获和处理运行时错误,提高程序的健壮性。
难点赘述:异常传播、raise语句和异常链。
- 异常传播:
def func1():
func2()
def func2():
func3()
def func3():
x = 10 / 0 # 抛出ZeroDivisionError
try:
func1()
except ZeroDivisionError:
print("捕获到异常:除数不能为零")
-
- 异常未被当前函数处理时,会向上层调用函数传播;
- 直到找到处理该异常的except块或程序终止。
- raise语句:
def divide(a, b):
if b == 0:
raise ZeroDivisionError("除数不能为零")
return a / b
try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(e) # 输出:除数不能为零
# 重新抛出异常
try:
x = int("abc")
except ValueError as e:
print("值错误:", e)
raise # 重新抛出捕获的异常
-
- 主动抛出异常;
- 重新抛出捕获的异常。
- 异常链:
try:
x = 10 / 0
except ZeroDivisionError as e:
raise ValueError("无效的输入") from e # 建立异常链
-
- 在处理一个异常时,可以引发另一个异常,形成异常链;
- 使用raise ... from ...语法显式建立异常链。
- 上下文管理器与异常:
class FileHandler:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# 返回True表示异常已处理,不继续传播
return True
# 使用自定义上下文管理器
with FileHandler("test.txt", "w") as f:
f.write("Hello, World!")
# 故意引发异常
x = 10 / 0
-
- 上下文管理器的__exit__方法可以处理异常;
- 使用with语句管理资源时,可以在上下文管理器中处理异常。
5.5 Python 自定义异常
核心内容:用户可以自定义异常类,用于特定业务逻辑的错误处理。
难点赘述:自定义异常的设计原则和使用场景。
- 自定义异常的定义:
class CustomError(Exception):
"""自定义异常的基类"""
pass
class ValidationError(CustomError):
"""验证错误异常"""
def __init__(self, message, errors):
super().__init__(message)
self.errors = errors
class DatabaseError(CustomError):
"""数据库操作异常"""
def __init__(self, message, code):
super().__init__(message)
self.code = code
- 自定义异常的使用:
def validate_user_input(data):
if not data:
raise ValidationError("输入数据不能为空", errors=["data"])
# 其他验证逻辑...
try:
validate_user_input("")
except ValidationError as e:
print(e) # 输出:输入数据不能为空
print("错误详情:", e.errors)
- 自定义异常的最佳实践:
- 异常类应继承自Exception类;
- 提供有意义的错误消息;
- 包含足够的上下文信息(如错误代码、错误详情);
- 在适当的层次抛出异常,避免在底层模块处理高层异常。
六、Python 对象和类
6.1 Python 面向对象编程
核心内容:面向对象编程(OOP)是一种编程范式,将数据和操作数据的方法封装在对象中。
难点赘述:面向对象的四大特性(封装、继承、多态、抽象)和类与实例的关系。
- 面向对象特性:
- 封装:将数据和方法封装在类中,通过访问修饰符(如__)控制访问权限;
- 继承:子类可以继承父类的属性和方法;
- 多态:不同子类对同一方法的不同实现;
- 抽象:通过抽象类定义接口,强制子类实现特定方法。
- 类与实例:
# 定义类
class Person:
def __init__(self, name, age):
self.name = name # 公有属性
self.__age = age # 私有属性
def say_hello(self): # 公有方法
print(f"你好,我是{self.name},今年{self.__age}岁。")
def __private_method(self): # 私有方法
print("这是一个私有方法")
# 创建实例
alice = Person("Alice", 30)
alice.say_hello() # 输出:你好,我是Alice,今年30岁。
# 访问私有属性和方法(不推荐)
print(alice._Person__age) # 输出:30(通过名称改写访问)
alice._Person__private_method() # 输出:这是一个私有方法
-
- 类:对象的模板,定义属性和方法;
- 实例:类的具体对象,拥有类定义的属性和方法。
- 类属性与实例属性:
class Car:
wheels = 4 # 类属性
def __init__(self, brand):
self.brand = brand # 实例属性
print(Car.wheels) # 输出:4
car1 = Car("Toyota")
car2 = Car("Honda")
print(car1.wheels) # 输出:4
print(car2.wheels) # 输出:4
# 修改类属性
Car.wheels = 5
print(car1.wheels) # 输出:5
print(car2.wheels) # 输出:5
# 实例属性会覆盖类属性
car1.wheels = 6
print(car1.wheels) # 输出:6(实例属性)
print(car2.wheels) # 输出:5(类属性)
-
- 类属性:所有实例共享的属性;
- 实例属性:每个实例独有的属性。
6.2 Python 类和对象
核心内容:类是对象的模板,对象是类的实例。
难点赘述:构造方法、析构方法和特殊方法(魔术方法)。
- 构造方法(__init__):
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Alice", 30)
print(person.name) # 输出:Alice
print(person.age) # 输出:30
-
- 在实例创建时自动调用;
- 用于初始化实例的属性。
- 析构方法(__del__):
class Resource:
def __init__(self):
print("资源初始化")
def __del__(self):
print("资源清理")
r = Resource() # 输出:资源初始化
del r # 输出:资源清理(当引用计数为零时)
-
- 在实例被销毁时自动调用;
- 用于资源清理。
- 特殊方法(魔术方法):
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1) # 输出:Vector(1, 2)
v3 = v1 + v2
print(v3) # 输出:Vector(4, 6)
print(v1 == v2) # 输出:False
-
- 以双下划线开头和结尾的方法,如__str__、__repr__、__add__等;
- 允许类的实例支持内置操作(如打印、相加、比较等)。
- 类方法和静态方法:
class MathUtils:
@classmethod
def add(cls, a, b):
return a + b
@staticmethod
def multiply(a, b):
return a * b
print(MathUtils.add(2, 3)) # 输出:5
print(MathUtils.multiply(2, 3)) # 输出:6
-
- 类方法:使用@classmethod装饰器,第一个参数是类对象;
- 静态方法:使用@staticmethod装饰器,无特殊参数。
6.3 Python 继承
核心内容:继承是面向对象编程的重要特性,允许子类继承父类的属性和方法。
难点赘述:单继承、多继承和方法重写。
- 单继承:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass # 抽象方法,由子类实现
class Dog(Animal):
def speak(self):
return "汪汪"
class Cat(Animal):
def speak(self):
return "喵喵"
dog = Dog("旺财")
print(dog.name) # 输出:旺财
print(dog.speak()) # 输出:汪汪
# 使用super()调用父类方法
class Animal:
def __init__(self, name):
self.name = name
self.legs = 4
def move(self):
print(f"{self.name} 用{self.legs}条腿移动")
class Bird(Animal):
def __init__(self, name):
super().__init__(name)
self.legs = 2
def move(self):
super().move() # 调用父类的move方法
print(f"{self.name} 还会飞")
bird = Bird("鹦鹉")
bird.move() # 输出:鹦鹉 用2条腿移动,鹦鹉 还会飞
-
- 子类继承一个父类;
- 使用super()函数调用父类的方法。
- 多继承:
class A:
def method(self):
print("A的方法")
class B:
def method(self):
print("B的方法")
class C(A, B):
pass
c = C()
c.method() # 输出:A的方法(根据MRO,先查找A)
print(C.__mro__) # 输出:(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
-
- 子类继承多个父类;
- 注意方法解析顺序(MRO)。
- 方法重写:
class Vehicle:
def start(self):
print("车辆启动")
class Car(Vehicle):
def start(self):
print("汽车启动")
super().start() # 调用父类的start方法
car = Car()
car.start() # 输出:汽车启动,车辆启动
-
- 子类重新定义父类的方法;
- 使用super()调用父类的方法。
- 多重继承的菱形问题:
class A:
def method(self):
print("A的方法")
class B(A):
def method(self):
print("B的方法")
super().method()
class C(A):
def method(self):
print("C的方法")
super().method()
class D(B, C):
pass
d = D()
d.method() # 输出:B的方法,C的方法,A的方法(根据MRO)
-
- 当多个父类有共同的祖先时,可能导致方法被多次调用;
- 使用super()和正确的 MRO 解决菱形问题。
6.4 Python 多重继承
核心内容:多重继承允许子类继承多个父类的属性和方法。
难点赘述:多重继承的复杂性、方法解析顺序和 Mixin 模式。
- 方法解析顺序(MRO):
class A:
def method(self):
print("A的方法")
class B(A):
def method(self):
print("B的方法")
super().method()
class C(A):
def method(self):
print("C的方法")
super().method()
class D(B, C):
pass
print(D.__mro__) # 输出:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
d = D()
d.method() # 输出:B的方法,C的方法,A的方法
-
- Python 使用 C3 线性化算法确定方法解析顺序;
- 可以通过__mro__属性或mro()方法查看 MRO。
- Mixin 模式:
class LoggingMixin:
def log(self, message):
print(f"日志:{message}")
class Calculator(LoggingMixin):
def add(self, a, b):
self.log(f"执行加法:{a} + {b}")
return a + b
class Database(LoggingMixin):
def connect(self):
self.log("连接数据库")
# 数据库连接逻辑...
calc = Calculator()
print(calc.add(2, 3)) # 输出:日志:执行加法:2 + 3,5
db = Database()
db.connect() # 输出:日志:连接数据库
-
- 使用多重继承实现 Mixin 类,为多个不相关的类提供功能;
- Mixin 类通常不单独使用,而是与其他类组合。
- 多重继承的最佳实践:
- 避免复杂的多重继承关系;
- 优先使用组合而非继承;
- Mixin 类应该功能单一,不依赖其他类;
- 在多重继承中明确指定父类的顺序。
6.5 Python 运算符重载
核心内容:运算符重载允许自定义类的实例使用内置运算符(如+、-、*、/等)。
难点赘述:运算符重载的方法和使用场景。
- 算术运算符重载:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = 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):
return self * scalar
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2
print(v3.x, v3.y) # 输出:4 6
v4 = v1 * 2
print(v4.x, v4.y) # 输出:2 4
v5 = 2 * v1 # 由于定义了__rmul__,可以交换顺序
print(v5.x, v5.y) # 输出:2 4
-
- __add__():+运算符;
- __sub__():-运算符;
- __mul__():*运算符;
- __truediv__():/运算符;
- __floordiv__()://运算符;
- __mod__():%运算符;
- __pow__():**运算符。
- 比较运算符重载:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __lt__(self, other):
return self.x < other.x or (self.x == other.x and self.y < other.y)
p1 = Point(1, 2)
p2 = Point(1, 3)
p3 = Point(2, 1)
print(p1 == p2) # 输出:False
print(p1 < p2) # 输出:True
print(p1 < p3) # 输出:True
-
- __eq__():==运算符;
- __ne__():!=运算符;
- __lt__():<运算符;
- __le__():<=运算符;
- __gt__():>运算符;
- __ge__():>=运算符。
- 其他运算符重载:
class MyList:
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __contains__(self, item):
return item in self.data
ml = MyList([1, 2, 3, 4, 5])
print(len(ml)) # 输出:5
print(ml[2]) # 输出:3
ml[2] = 10
print(ml.data) # 输出:[1, 2, 10, 4, 5]
print(10 in ml) # 输出:True
-
- 成员运算符:__contains__();
- 索引和切片:__getitem__()、__setitem__();
- 长度:__len__();
- 字符串表示:__str__()、__repr__()。
- 运算符重载的最佳实践:
- 运算符重载应保持与内置类型一致的行为;
- 只重载有意义的运算符;
- 避免过度使用运算符重载,影响代码可读性;
- 在文档字符串中说明运算符的行为。
七、Python 日期和时间
7.1 Python 日期时间 (datetime)
核心内容:Python 的datetime模块提供了处理日期和时间的类,包括date、time、datetime、timedelta等。
难点赘述:日期时间对象的创建、格式化和时间差计算。
- 日期时间对象的创建:
from datetime import date, time, datetime
# 创建date对象
d = date(2023, 10, 1)
print(d) # 输出:2023-10-01
# 创建time对象
t = time(12, 30, 45)
print(t) # 输出:12:30:45
# 创建datetime对象
dt = datetime(2023, 10, 1, 12, 30, 45)
print(dt) # 输出:2023-10-01 12:30:45
# 获取当前日期时间
now = datetime.now()
print(now) # 输出类似:2023-10-01 15:30:45.123456
# 从timestamp创建datetime对象
timestamp = 1696150245
dt = datetime.fromtimestamp(timestamp)
print(dt) # 输出:2023-10-01 12:30:45
- 日期时间的属性和方法:
from datetime import datetime
dt = datetime(2023, 10, 1, 12, 30, 45)
print(dt.year) # 输出:2023
print(dt.month) # 输出:10
print(dt.day) # 输出:1
print(dt.hour) # 输出:12
print(dt.minute) # 输出:30
print(dt.second) # 输出:45
# 替换部分属性
dt2 = dt.replace(hour=15, minute=0)
print(dt2) # 输出:2023-10-01 15:00:45
# 日期时间比较
dt1 = datetime(2023, 10, 1, 12, 0, 0)
dt2 = datetime(2023, 10, 1, 15, 0, 0)
print(dt1 < dt2) # 输出:True
- 时间差计算:
from datetime import datetime, timedelta
dt = datetime(2023, 10, 1, 12, 30, 45)
delta = timedelta(days=7, hours=3, minutes=30)
# 日期时间加上时间差
future_dt = dt + delta
print(future_dt) # 输出:2023-10-08 16:00:45
# 日期时间减去时间差
past_dt = dt - delta
print(past_dt) # 输出:2023-09-24 09:00:45
# 计算两个日期时间的差
dt1 = datetime(2023, 10, 1, 12, 0, 0)
dt2 = datetime(2023, 10, 1, 15, 30, 0)
diff = dt2 - dt1
print(diff) # 输出:3:30:00
print(diff.total_seconds()) # 输出:12600.0(总秒数)
-
- 使用timedelta类表示时间间隔;
- 可以进行日期时间的加减运算。
7.2 Python strftime()
核心内容:strftime()方法将datetime对象格式化为字符串。
难点赘述:格式代码的使用和本地化设置。
- 基本格式代码:
from datetime import datetime
dt = datetime(2023, 10, 1, 12, 30, 45)
print(dt.strftime("%Y-%m-%d %H:%M:%S")) # 输出:2023-10-01 12:30:45
print(dt.strftime("%a, %b %d %Y")) # 输出:Sun, Oct 01 2023
print(dt.strftime("今天是%A,%B %d, %Y")) # 输出:今天是Sunday,October 01, 2023
-
- %Y:四位数年份;
- %m:两位数月份(01-12);
- %d:两位数日期(01-31);
- %H:24 小时制小时(00-23);
- %M:分钟(00-59);
- %S:秒(00-59);
- %a:星期几的缩写(如 Mon);
- %A:星期几的全称(如 Monday);
- %b:月份的缩写(如 Oct);
- %B:月份的全称(如 October);
- %c:本地日期时间表示;
- %x:本地日期表示;
- %X:本地时间表示。
- 本地化格式:
import locale
from datetime import datetime
# 设置本地化环境为中文(需要系统支持)
locale.setlocale(locale.LC_ALL, "zh_CN.UTF-8")
dt = datetime(2023, 10, 1, 12, 30, 45)
print(dt.strftime("%c")) # 输出:2023年10月01日 周日 12:30:45
print(dt.strftime("%x")) # 输出:2023/10/01
print(dt.strftime("%X")) # 输出:12:30:45
-
- 不同地区的日期时间格式可能不同;
- 使用locale模块设置本地化环境。
- 自定义格式组合:
from datetime import datetime
dt = datetime(2023, 10, 1, 12, 30, 45)
print(dt.strftime("%Y年%m月%d日 %H时%M分%S秒")) # 输出:2023年10月01日 12时30分45秒
print(dt.strftime("%Y-%m-%dT%H:%M:%S")) # 输出:2023-10-01T12:30:45(ISO 8601格式)
-
- 可以组合多个格式代码创建自定义的日期时间字符串。
7.3 Python strptime()
核心内容:strptime()方法将字符串解析为datetime对象。
难点赘述:解析格式的匹配和异常处理。
- 基本解析方法:
from datetime import datetime
s = "2023-10-01 12:30:45"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
print(dt) # 输出:2023-10-01 12:30:45
s = "2023/10/01"
dt = datetime.strptime(s, "%Y/%m/%d")
print(dt) # 输出:2023-10-01 00:00:00
s = "Oct 1, 2023 12:30 PM"
dt = datetime.strptime(s, "%b %d, %Y %I:%M %p")
print(dt) # 输出:2023-10-01 12:30:00
-
- 使用与strftime()相同的格式代码;
- datetime.strptime(字符串, 格式)。
- 本地化解析:
import locale
from datetime import datetime
# 设置本地化环境为中文(需要系统支持)
locale.setlocale(locale.LC_ALL, "zh_CN.UTF-8")
s = "2023年10月01日 12:30:45"
dt = datetime.strptime(s, "%Y年%m月%d日 %H:%M:%S")
print(dt) # 输出:2023-10-01 12:30:45
s = "星期日, 2023年10月01日"
dt = datetime.strptime(s, "%A, %Y年%m月%d日")
print(dt) # 输出:2023-10-01 00:00:00
-
- 解析本地化日期时间字符串时,需要设置相应的本地化环境;
- 不同地区的月份和星期名称可能不同。
- 异常处理:
from datetime import datetime
s = "2023-13-01" # 无效的月份
try:
dt = datetime.strptime(s, "%Y-%m-%d")
except ValueError as e:
print("解析错误:", e) # 输出:解析错误: day is out of range for month
-
- 字符串格式与指定格式不匹配时,会抛出ValueError;
- 使用try-except块处理解析错误。
7.4 Python 当前日期和时间
核心内容:获取当前日期和时间的方法。
难点赘述:时区处理和不同精度的时间获取。
- 获取当前日期时间:
from datetime import datetime, date
# 获取当前日期时间(本地时间)
now = datetime.now()
print("当前日期时间:", now)
# 获取当前UTC日期时间
utc_now = datetime.utcnow()
print("当前UTC日期时间:", utc_now)
# 获取当前日期
today = date.today()
print("当前日期:", today)
-
- datetime.now():获取当前日期时间(本地时间);
- datetime.utcnow():获取当前 UTC 日期时间;
- date.today():获取当前日期。
- 时区处理:
from datetime import datetime
import pytz # 需要安装pytz库
# 获取当前北京时间
beijing_tz = pytz.timezone("Asia/Shanghai")
beijing_now = datetime.now(beijing_tz)
print("北京时间:", beijing_now)
# 获取当前纽约时间
new_york_tz = pytz.timezone("America/New_York")
new_york_now = datetime.now(new_york_tz)
print("纽约时间:", new_york_now)
# 时区转换
dt = datetime.now(beijing_tz)
dt_utc = dt.astimezone(pytz.utc)
print("转换为UTC时间:", dt_utc)
-
- Python 的datetime模块对时区的支持有限;
- 使用第三方库pytz或zoneinfo(Python 3.9+)处理时区。
- 时间戳:
from datetime import datetime
# 获取当前时间戳(浮点数,秒)
timestamp = datetime.now().timestamp()
print("当前时间戳:", timestamp)
# 将时间戳转换为datetime对象
dt = datetime.fromtimestamp(timestamp)
print("对应的日期时间:", dt)
# UTC时间戳
utc_timestamp = datetime.utcnow().timestamp()
print("UTC时间戳:", utc_timestamp)
-
- 获取当前时间戳(从 1970 年 1 月 1 日 UTC 到现在的秒数)。
7.5 Python 获取当前时间
核心内容:获取当前时间的不同方法。
难点赘述:时间精度和性能考虑。
- 获取当前时间:
from datetime import datetime
import time
# 获取当前时间(time对象)
current_time = datetime.now().time()
print("当前时间:", current_time)
# 获取当前时间戳(秒)
timestamp = time.time()
print("当前时间戳:", timestamp)
# 获取当前时间的字符串表示
current_time_str = time.ctime()
print("当前时间字符串:", current_time_str)
-
- datetime.now().time():获取当前时间(time对象);
- time模块的time()函数:获取当前时间戳;
- time模块的ctime()函数:获取当前时间的字符串表示。
- 时间精度:
import time
# 测量代码执行时间
start = time.perf_counter()
# 执行一些操作
end = time.perf_counter()
print("执行时间:", end - start, "秒")
# 获取CPU时间
cpu_time = time.process_time()
print("CPU时间:", cpu_time, "秒")
-
- time.time()返回的时间戳精度为秒或毫秒(取决于系统);
- 使用time.perf_counter()或time.process_time()获取高精度时间。
- 时间格式转换:
from datetime import datetime, time
# 将time对象格式化为字符串
t = datetime.now().time()
print(t.strftime("%H:%M:%S")) # 输出:当前时间的HH:MM:SS格式
# 将字符串解析为time对象
s = "12:30:45"
t = datetime.strptime(s, "%H:%M:%S").time()
print(t) # 输出:12:30:45
-
- 将time对象格式化为字符串;
- 将字符串解析为time对象。
7.6 Python 时间戳 (timestamp)
核心内容:时间戳是从 1970 年 1 月 1 日 UTC 到指定时间的秒数。
难点赘述:时间戳与datetime对象的转换和时区问题。
- 时间戳与datetime转换:
from datetime import datetime
# 创建datetime对象
dt = datetime(2023, 10, 1, 12, 30, 45)
# 转换为时间戳
timestamp = dt.timestamp()
print("时间戳:", timestamp) # 输出:1696150245.0
# 将时间戳转换为本地datetime对象
dt_local = datetime.fromtimestamp(timestamp)
print("本地时间:", dt_local) # 输出:2023-10-01 12:30:45
# 将时间戳转换为UTC datetime对象
dt_utc = datetime.utcfromtimestamp(timestamp)
print("UTC时间:", dt_utc) # 输出:2023-10-01 04:30:45(假设时区为UTC+8)
-
- datetime.timestamp():将datetime对象转换为时间戳;
- datetime.fromtimestamp():将时间戳转换为datetime对象(本地时间);
- datetime.utcfromtimestamp():将时间戳转换为 UTCdatetime对象。
- 时区与时间戳:
from datetime import datetime
import pytz # 需要安装pytz库
# 获取当前北京时间的时间戳
beijing_tz = pytz.timezone("Asia/Shanghai")
beijing_now = datetime.now(beijing_tz)
timestamp = beijing_now.timestamp()
print("北京时间戳:", timestamp)
# 将时间戳转换为UTC时间
dt_utc = datetime.utcfromtimestamp(timestamp)
print("UTC时间:", dt_utc)
# 将时间戳转换为其他时区的时间
new_york_tz = pytz.timezone("America/New_York")
new_york_dt = datetime.fromtimestamp(timestamp, new_york_tz)
print("纽约时间:", new_york_dt)
-
- 时间戳是 UTC 时间的表示,与时区无关;
- 将时间戳转换为本地时间时,需考虑时区偏移。
- 时间戳的精度:
import time
# 获取高精度时间戳(可能包含毫秒)
timestamp = time.time()
print("高精度时间戳:", timestamp)
-
- 时间戳通常为浮点数,精确到秒或毫秒;
- 不同系统的时间戳精度可能不同。
7.7 Python time 模块
核心内容:time模块提供了处理时间的函数,包括时间戳、睡眠和性能测量。
难点赘述:time模块的函数与datetime模块的区别和使用场景。
- 时间获取函数:
import time
# 获取当前时间戳
timestamp = time.time()
print("时间戳:", timestamp)
# 获取当前时间的字符串表示
current_time_str = time.ctime()
print("当前时间字符串:", current_time_str)
# 将时间戳转换为UTC时间的struct_time
utc_time = time.gmtime(timestamp)
print("UTC时间:", utc_time)
# 将时间戳转换为本地时间的struct_time
local_time = time.localtime(timestamp)
print("本地时间:", local_time)
-
- time.time():获取当前时间戳;
- time.ctime():获取当前时间的字符串表示;
- time.gmtime():将时间戳转换为 UTC 时间的 struct_time;
- time.localtime():将时间戳转换为本地时间的 struct_time。
- 时间格式化:
import time
# 获取当前时间的struct_time
now = time.localtime()
# 格式化为字符串
print(time.strftime("%Y-%m-%d %H:%M:%S", now)) # 输出:当前日期时间的字符串表示
# 将字符串解析为struct_time
s = "2023-10-01 12:30:45"
struct_time = time.strptime(s, "%Y-%m-%d %H:%M:%S")
print(struct_time) # 输出:time.struct_time(tm_year=2023, tm_mon=10, tm_mday=1, tm_hour=12, tm_min=30, tm_sec=45, ...)
-
- time.strftime():将 struct_time 格式化为字符串;
- time.strptime():将字符串解析为 struct_time。
- 性能测量和睡眠:
import time
# 测量代码执行时间
start = time.perf_counter()
time.sleep(1) # 暂停1秒
end = time.perf_counter()
print("执行时间:", end - start, "秒")
# 测量CPU时间
cpu_start = time.process_time()
# 执行一些CPU密集型操作
cpu_end = time.process_time()
print("CPU时间:", cpu_end - cpu_start, "秒")
-
- time.perf_counter():测量性能的高精度计时器;
- time.process_time():测量 CPU 时间;
- time.sleep():暂停执行指定秒数。
7.8 Python sleep()
核心内容:time.sleep()函数暂停当前线程的执行指定秒数。
难点赘述:睡眠的精度和中断处理。
- 基本用法:
import time
print("开始睡眠")
time.sleep(2) # 暂停2秒
print("睡眠结束")
- 睡眠的精度:
import time
start = time.perf_counter()
time.sleep(1) # 期望暂停1秒
end = time.perf_counter()
print("实际暂停时间:", end - start, "秒") # 可能略大于1秒
-
- sleep()的实际暂停时间可能比指定时间长;
- 受系统调度和其他因素影响。
- 中断处理:
import time
try:
print("开始长时间睡眠")
time.sleep(3600) # 暂停1小时
except KeyboardInterrupt:
print("睡眠被用户中断")
-
- 在睡眠期间,线程可以被信号中断;
- 处理KeyboardInterrupt异常。
- 睡眠的应用场景:
import time
# 控制循环每秒钟执行一次
while True:
print("执行任务")
time.sleep(1) # 暂停1秒
# 模拟网络请求延迟
print("发送请求...")
time.sleep(2) # 模拟2秒的网络延迟
print("请求完成")
-
- 控制循环的执行频率;
- 模拟长时间操作;
- 在多线程程序中协调线程执行。
八、Python 高级知识
8.1 Python 命名空间和作用域
核心内容:命名空间是变量的容器,作用域是变量可以被访问的区域。
难点赘述:命名空间的生命周期、作用域链和闭包的作用域问题。
- 命名空间分类:
# 内置命名空间示例
print(len.__class__) # 输出:<class 'builtin_function_or_method'>
# 全局命名空间示例
global_var = 10
def func():
# 局部命名空间示例
local_var = 20
print(locals()) # 输出:{'local_var': 20}
print(globals()) # 输出:包含全局变量的字典
func()
-
- 内置命名空间:包含 Python 内置函数和类型;
- 全局命名空间:模块级别的变量;
- 局部命名空间:函数或类内部的变量。
- 作用域链:
global_var = "全局变量"
def outer():
outer_var = "外部函数变量"
def inner():
inner_var = "内部函数变量"
print(inner_var) # 本地作用域
print(outer_var) # 外部函数作用域
print(global_var) # 全局作用域
print(len) # 内置作用域
inner()
outer()
-
- Python 查找变量时遵循 LEGB 规则:Local → Enclosing → Global → Built-in;
- 闭包中的变量查找会访问外部函数的命名空间。
- 闭包的作用域问题:
def create_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c1 = create_counter()
print(c1()) # 输出:1
print(c1()) # 输出:2
# 循环中创建闭包的问题
functions = []
for i in range(3):
functions.append(lambda: i)
for f in functions:
print(f()) # 输出:2, 2, 2(所有闭包共享同一个i)
# 解决方案:使用默认参数捕获当前值
functions = []
for i in range(3):
functions.append(lambda x=i: x)
for f in functions:
print(f()) # 输出:0, 1, 2
-
- 闭包中的变量在外部函数返回后仍然存在;
- 循环中创建闭包时,可能共享同一变量。
8.2 Python 迭代器
核心内容:迭代器是实现了__iter__()和__next__()方法的对象,可以逐个访问集合中的元素。
难点赘述:迭代器的创建、使用和终止条件。
- 迭代器协议:
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
# 使用迭代器
it = MyIterator([1, 2, 3, 4, 5])
print(next(it)) # 输出:1
print(next(it)) # 输出:2
print(next(it)) # 输出:3
print(next(it)) # 输出:4
print(next(it)) # 输出:5
# print(next(it)) # 抛出StopIteration异常
-
- 迭代器对象必须实现__iter__()方法,返回迭代器自身;
- 必须实现__next__()方法,返回下一个元素,无元素时抛出StopIteration异常。
- 可迭代对象与迭代器:
# 列表是可迭代对象
my_list = [1, 2, 3]
it = iter(my_list) # 获取迭代器
print(next(it)) # 输出:1
print(next(it)) # 输出:2
print(next(it)) # 输出:3
-
- 可迭代对象是实现了__iter__()方法的对象,可以生成迭代器;
- 迭代器是可迭代对象的具体实现。
- 迭代器的应用场景:
# 惰性生成斐波那契数列
class FibonacciIterator:
def __init__(self, max_num):
self.max_num = max_num
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
if self.curr > self.max_num:
raise StopIteration
value = self.curr
self.prev, self.curr = self.curr, self.prev + self.curr
return value
fib = FibonacciIterator(100)
for num in fib:
print(num, end=" ") # 输出:1 1 2 3 5 8 13 21 34 55 89
-
- 自定义数据结构的遍历;
- 惰性计算(按需生成元素);
- 内存高效的数据处理。
8.3 Python 生成器
核心内容:生成器是一种特殊的迭代器,使用yield语句生成值,简化迭代器的创建。
难点赘述:生成器表达式、生成器函数和生成器的状态管理。
- 生成器函数:
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # 输出:1
print(next(gen)) # 输出:2
print(next(gen)) # 输出:3
# print(next(gen)) # 抛出StopIteration异常
-
- 使用yield语句返回值,函数会暂停执行并保存状态;
- 再次调用时从暂停处继续执行。
- 生成器表达式:
# 生成器表达式
gen = (x**2 for x in range(1, 6))
print(next(gen)) # 输出:1
print(next(gen)) # 输出:4
print(next(gen)) # 输出:9
print(next(gen)) # 输出:16
print(next(gen)) # 输出:25
-
- 简洁的生成器创建方式,类似于列表推导式;
- 使用圆括号而非方括号。
- 生成器的应用场景:
# 生成无限序列(如斐波那契数列)
def fibonacci():
a, b = 0, 1
while True:
yield b
a, b = b, a + b
gen = fibonacci()
for _ in range(10):
print(next(gen), end=" ") # 输出:1 1 2 3 5 8 13 21 34 55
# 处理大文件
def read_large_file(filepath):
with open(filepath, "r") as f:
for line in f:
yield line.strip()
for line in read_large_file("large_file.txt"):
process(line) # 处理每一行,内存高效
-
- 惰性计算大数据集;
- 无限序列的生成;
- 内存高效的数据处理。
- 生成器的状态和控制:
def counter():
count = 0
while True:
command = yield count
if command == "inc":
count += 1
elif command == "dec":
count -= 1
elif command == "reset":
count = 0
gen = counter()
print(next(gen)) # 输出:0
print(gen.send("inc")) # 输出:1
print(gen.send("inc")) # 输出:2
print(gen.send("dec")) # 输出:1
print(gen.send("reset")) # 输出:0
gen.close()
-
- send()方法可以向生成器发送值;
- throw()方法可以在生成器中抛出异常;
- close()方法可以关闭生成器。
8.4 Python 闭包
核心内容:闭包是函数和其所在环境的组合,允许函数访问和修改外部函数的变量。
难点赘述:闭包的创建、作用域问题和应用场景。
- 闭包的定义:
def outer(x):
def inner(y):
return x + y
return inner
add_5 = outer(5)
print(add_5(3)) # 输出:8
print(add_5(5)) # 输出:10
-
- 闭包由内部函数和其引用的外部函数变量组成;
- 内部函数在外部函数返回后仍然可以访问外部函数的变量。
- 闭包的作用域问题:
def counter():
count = 0
def increment():
nonlocal count # 声明使用外部函数的变量
count += 1
return count
return increment
c = counter()
print(c()) # 输出:1
print(c()) # 输出:2
print(c()) # 输出:3
-
- 闭包中的变量在外部函数返回后仍然存在;
- 闭包中的变量是不可变对象时,无法直接修改;
- 使用nonlocal关键字声明变量为非局部变量。
- 闭包的应用场景:
# 工厂函数示例
def make_multiplier(factor):
def multiplier(x):
return x * factor
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 输出:10
print(triple(5)) # 输出:15
# 数据封装示例
def bank_account(initial_balance):
balance = initial_balance
def deposit(amount):
nonlocal balance
balance += amount
return balance
def withdraw(amount):
nonlocal balance
if amount > balance:
return "余额不足"
balance -= amount
return balance
return {"deposit": deposit, "withdraw": withdraw}
account = bank_account(1000)
print(account["deposit"](500)) # 输出:1500
print(account["withdraw"](200)) # 输出:1300
print(account["withdraw"](2000)) # 输出:余额不足
-
- 创建工厂函数(动态生成函数);
- 实现装饰器;
- 数据封装和状态保持。
- 闭包与装饰器的关系:
def decorator(func):
def wrapper():
print("装饰器执行前")
func()
print("装饰器执行后")
return wrapper
@decorator
def greet():
print("你好,世界!")
greet() # 输出:装饰器执行前,你好,世界!,装饰器执行后
-
- 装饰器本质上是闭包的应用;
- 装饰器函数返回一个新的函数,该函数引用了装饰器的参数和原始函数。
8.5 Python 装饰器
核心内容:装饰器是一种特殊的闭包,用于修改函数或类的行为,而无需直接修改其源代码。
难点赘述:装饰器的定义、带参数的装饰器和类装饰器。
- 基本装饰器:
def decorator(func):
def wrapper():
print("执行前")
func()
print("执行后")
return wrapper
@decorator
def greet():
print("你好,世界!")
greet() # 输出:执行前,你好,世界!,执行后
-
- 装饰器函数接收被装饰的函数作为参数,并返回一个新的函数;
- 使用@语法糖应用装饰器。
- 带参数的装饰器:
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello() # 输出:Hello!三次
-
- 装饰器工厂函数返回实际的装饰器;
- 可以传递参数给装饰器。
- 类装饰器:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"函数被调用了{self.count}次")
return self.func(*args, **kwargs)
@CountCalls
def greet():
print("你好!")
greet() # 输出:函数被调用了1次,你好!
greet() # 输出:函数被调用了2次,你好!
greet() # 输出:函数被调用了3次,你好!
-
- 使用类作为装饰器,实现__call__方法;
- 类装饰器可以保持状态。
- 装饰器的应用场景:
# 日志记录装饰器
def log_execution(func):
def wrapper(*args, **kwargs):
print(f"执行函数:{func.__name__}")
result = func(*args, **kwargs)
print(f"函数{func.__name__}执行完毕,结果:{result}")
return result
return wrapper
@log_execution
def add(a, b):
return a + b
print(add(3, 5)) # 输出:执行函数:add,函数add执行完毕,结果:8,8
# 性能分析装饰器
import time
def measure_time(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__}执行时间:{end - start:.4f}秒")
return result
return wrapper
@measure_time
def heavy_task():
time.sleep(1)
heavy_task() # 输出:heavy_task执行时间:1.000x秒
-
- 日志记录;
- 性能分析;
- 权限验证;
- 缓存;
- 输入验证。
- 装饰器的最佳实践:
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""包装器函数文档字符串"""
print("执行前")
result = func(*args, **kwargs)
print("执行后")
return result
return wrapper
@decorator
def greet():
"""被装饰函数文档字符串"""
print("你好,世界!")
print(greet.__name__) # 输出:greet(而不是wrapper)
print(greet.__doc__) # 输出:被装饰函数文档字符串
-
- 使用functools.wraps装饰器保留被装饰函数的元数据;
- 为装饰器提供清晰的文档字符串;
- 避免过度使用装饰器,保持代码简洁。
8.6 Python @property
核心内容:@property装饰器将类的方法转换为属性,允许像访问属性一样调用方法。
难点赘述:@property的使用、setter和deleter的定义。
- 基本用法:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@property
def area(self):
return 3.14159 * self._radius ** 2
c = Circle(5)
print(c.radius) # 输出:5
print(c.area) # 输出:78.53975
# c.area = 100 # 报错:can't set attribute 'area'
-
- 将方法转换为只读属性;
- 使用@property装饰器。
- setter和deleter方法:
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError("姓名必须是字符串")
self._name = value
@name.deleter
def name(self):
del self._name
p = Person("Alice")
print(p.name) # 输出:Alice
p.name = "Bob"
print(p.name) # 输出:Bob
# p.name = 123 # 抛出TypeError异常
del p.name
# print(p.name) # 抛出AttributeError异常
-
- 使用@属性名.setter装饰器定义设置方法;
- 使用@属性名.deleter装饰器定义删除方法。
- 计算属性的应用场景:
class Rectangle:
def __init__(self, width, height):
self._width = width
self._height = height
@property
def width(self):
return self._width
@width.setter
def width(self, value):
if value <= 0:
raise ValueError("宽度必须为正数")
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self, value):
if value <= 0:
raise ValueError("高度必须为正数")
self._height = value
@property
def area(self):
return self._width * self._height
r = Rectangle(5, 3)
print(r.area) # 输出:15
r.width = 6
r.height = 4
print(r.area) # 输出:24
-
- 隐藏实现细节;
- 数据验证;
- 延迟计算;
- 兼容性维护。
- @property与__getattr__的区别:
class DynamicAttributes:
def __init__(self):
self._data = {}
def __getattr__(self, name):
return self._data.get(name, None)
def __setattr__(self, name, value):
if name == "_data":
super().__setattr__(name, value)
else:
self._data[name] = value
obj = DynamicAttributes()
obj.x = 10
obj.y = 20
print(obj.x) # 输出:10
print(obj.z) # 输出:None
-
- @property用于特定属性的访问控制;
- __getattr__用于动态处理不存在的属性。
8.7 Python 正则表达式 (RegEx)
核心内容:正则表达式是用于匹配和操作字符串的强大工具,Python 通过re模块提供支持。
难点赘述:正则表达式的语法、匹配模式和高级应用。
- 基本匹配:
import re
s = "Hello, World!"
# 匹配开头
if re.match("Hello", s):
print("匹配成功") # 输出:匹配成功
# 搜索子串
if re.search("World", s):
print("找到World") # 输出:找到World
# 查找所有数字
s = "abc123def456"
numbers = re.findall(r"\d+", s)
print(numbers) # 输出:["123", "456"]
# 替换子串
s = "Hello, World!"
new_s = re.sub("World", "Python", s)
print(new_s) # 输出:Hello, Python!
-
- re.match():从字符串开头匹配;
- re.search():在字符串中查找匹配项;
- re.findall():查找所有匹配项;
- re.sub():替换匹配的字符串。
- 正则表达式语法:
# 匹配邮箱地址
email_pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
print(re.match(email_pattern, "user@example.com")) # 匹配成功
print(re.match(email_pattern, "user@example")) # 匹配失败
# 匹配电话号码
phone_pattern = r"^(\+?\d{1,3}[-.\s]?)?(\(\d{3}\)|\d{3})[-.\s]?\d{3}[-.\s]?\d{4}$"
print(re.match(phone_pattern, "+1 (123) 456-7890")) # 匹配成功
print(re.match(phone_pattern, "123-456-7890")) # 匹配成功
-
- 元字符:.、^、$、*、+、?、{}、[]、()、|;
- 字符类:\d、\w、\s、[a-z]、[^0-9];
- 量词:*、+、?、{n}、{n,}、{n,m};
- 分组和捕获:(...)、(?P<name>...)。
- 匹配模式:
s = """Hello,
World!
Python is fun!"""
# 匹配以大写字母开头的单词(忽略大小写)
pattern = r"\b[A-Z]\w+\b"
print(re.findall(pattern, s, re.I)) # 输出:["Hello", "World", "Python"]
# 匹配所有行的开头和结尾
pattern = r"^Hello|World!$"
print(re.findall(pattern, s, re.M)) # 输出:["Hello", "World!"]
# 点匹配包括换行符
pattern = r".+"
print(re.match(pattern, s, re.S).group()) # 输出整个字符串
# 详细模式
pattern = r"""
^(\+?\d{1,3})? # 可选的国家代码
[-.\s]? # 可选的分隔符
(\(\d{3}\)|\d{3}) # 区号
[-.\s]? # 可选的分隔符
\d{3} # 中间三位
[-.\s]? # 可选的分隔符
\d{4}$ # 最后四位
"""
print(re.match(pattern, "+1-123-456-7890", re.X)) # 匹配成功
-
- re.I:忽略大小写;
- re.M:多行模式;
- re.S:点匹配包括换行符;
- re.X:详细模式(允许注释和空格)。
- 高级应用:
# 匹配重复的单词
pattern = r"\b(\w+)\s+\1\b"
s = "hello hello world"
print(re.findall(pattern, s)) # 输出:["hello"]
# 匹配HTML标签
pattern = r"<(\w+)>(.*?)</\1>"
s = "<h1>标题</h1>"
print(re.match(pattern, s).groups()) # 输出:("h1", "标题")
# 前瞻断言
pattern = r"\d+(?=%)" # 匹配后面跟着%的数字
s = "20% discount, 10% off"
print(re.findall(pattern, s)) # 输出:["20", "10"]
# 命名分组
pattern = r"(?P<name>\w+), (?P<age>\d+)"
s = "Alice, 30"
match = re.match(pattern, s)
print(match.group("name")) # 输出:Alice
print(match.group("age")) # 输出:30
-
- 后向引用:\1、\2等;
- 前瞻和后顾断言;
- 命名分组:(?P<name>...)。
8.8 Python 随机模块 (Random)
核心内容:random模块提供了生成伪随机数的函数,用于随机选择和分布。
难点赘述:随机数的生成、种子设置和不同分布的应用。
- 基本随机函数:
import random
# 生成随机浮点数
print(random.random()) # 输出:0.123456789...(每次不同)
# 生成随机整数
print(random.randint(1, 10)) # 输出:1到10之间的整数
# 随机选择元素
fruits = ["apple", "banana", "cherry"]
print(random.choice(fruits)) # 输出:随机一个水果
# 打乱序列
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)
print(numbers) # 输出:打乱后的列表
-
- random():生成 0 到 1 之间的随机浮点数;
- randint(a, b):生成 [a, b] 之间的随机整数;
- choice(seq):从序列中随机选择一个元素;
- shuffle(seq):随机打乱序列。
- 随机种子:
import random
# 设置种子,确保每次生成相同的随机数序列
random.seed(42)
print(random.random()) # 输出:0.6394267984578837
print(random.randint(1, 10)) # 输出:14(因为seed(42)的randint范围可能不同)
# 不同种子生成不同的序列
random.seed(100)
print(random.random()) # 输出:0.7357088787345654
-
- seed()函数设置随机数生成器的种子,确保可复现的随机序列。
- 分布函数:
import random
# 均匀分布
print(random.uniform(1, 10)) # 输出:1到10之间的随机浮点数
# 正态分布
print(random.normalvariate(0, 1)) # 输出:均值为0,标准差为1的随机数
# 指数分布
print(random.expovariate(1)) # 输出:λ=1的指数分布随机数
-
- uniform(a, b):均匀分布;
- normalvariate(mu, sigma):正态分布;
- expovariate(lambd):指数分布;
- gammavariate(alpha, beta):伽马分布。
- 随机采样和排列:
import random
# 随机采样
population = range(1, 101)
sample = random.sample(population, 10)
print(sample) # 输出:10个不同的随机数
# 按权重选择
fruits = ["apple", "banana", "cherry"]
weights = [0.1, 0.3, 0.6]
print(random.choices(fruits, weights=weights, k=5)) # 输出:可能多次选择cherry
-
- sample(population, k):从总体中随机采样 k 个元素;
- choices(population, weights=None, k=1):按权重随机选择。
8.9 Python 数学模块 (Math)
核心内容:math模块提供了数学函数和常量,用于数值计算。
难点赘述:数学函数的应用、精度问题和特殊函数的使用。
- 数学常量:
import math
print(math.pi) # 输出:3.141592653589793
print(math.e) # 输出:2.718281828459045
print(math.inf) # 输出:inf
print(math.nan) # 输出:nan
-
- math.pi:圆周率 π;
- math.e:自然对数的底 e;
- math.inf:无穷大;
- math.nan:非数字(NaN)。
- 数值运算函数:
import math
# 三角函数
print(math.sin(math.pi / 2)) # 输出:1.0
print(math.cos(math.pi)) # 输出:-1.0
print(math.tan(math.pi / 4)) # 输出:1.0
# 指数和对数
print(math.exp(1)) # 输出:2.718281828459045
print(math.log(math.e)) # 输出:1.0
print(math.log10(100)) # 输出:2.0
# 幂和根
print(math.pow(2, 3)) # 输出:8.0
print(math.sqrt(25)) # 输出:5.0
# 取整
print(math.ceil(3.14)) # 输出:4
print(math.floor(3.99)) # 输出:3
print(round(3.5)) # 输出:4
-
- 三角函数:sin()、cos()、tan()、asin()、acos()、atan();
- 指数和对数:exp()、log()、log10();
- 幂和根:pow()、sqrt();
- 取整:ceil()、floor()、round();
- 绝对值:abs()。
- 特殊函数:
import math
# 阶乘
print(math.factorial(5)) # 输出:120
# 组合数
print(math.comb(5, 2)) # 输出:10
print(math.perm(5, 2)) # 输出:20
# 双曲函数
print(math.sinh(0)) # 输出:0.0
print(math.cosh(0)) # 输出:1.0
-
- 阶乘:math.factorial();
- 组合数:math.comb()、math.perm();
- 双曲函数:sinh()、cosh()、tanh();
- 误差函数:erf()、erfc()。
更多推荐




所有评论(0)