SICP_python_期中前笔记
2025年12月11日 · 2305 字 · 11 分钟
摘要
本文为《计算机程序的构造和解释》(SICP,CS61A 南大引进版)python部分期中前课程核心笔记,聚焦程序设计的底层逻辑与核心思想,旨在帮助学习者搭建“知其然更知其所以然”的编程认知框架。适合正在学习 CS61A 课程的学习者梳理知识体系,也可作为编程入门者夯实基础、理解程序设计本质的参考资料。
SICP_python_期中前笔记
易错提醒
- 记得初始化参数!
- 记得每个函数接受多少个输入!
- 记得return!
- lambda函数要在被()调用的时候才会执行!
- 被next拿出来的会在可变序列中消失!pop也是!可变序列在一处被修改后,所有对它的引用都会随之改变!!!
易忘函数
all(iterable)
- 功能:如果
iterable中所有元素为True,返回True,否则返回False。 - 注意:会短路,即遇到第一个
False时立即返回False。
any(iterable)
- 功能:如果
iterable中至少有一个元素为True,返回True,否则返回False。 - 注意:会短路,即遇到第一个
True时立即返回True。
enumerate(iterable, start=0)
- 功能:返回一个枚举对象,包含可迭代对象的索引和值。
- 注意:
start参数指定索引的起始值。
filter(function, iterable)
- 功能:根据
function函数过滤iterable中的元素,只有function返回True的元素会被保留。 - 注意:返回一个迭代器,需要转换为列表等容器查看结果。
pow(x, y, z=None)
- 功能:返回
x的y次幂。如果提供了z,则返回(x ** y) % z。 - 注意:该函数在处理大数时效率较高。
zip(*iterables)
- 功能:将多个可迭代对象按元素配对,返回一个包含元组的迭代器。
- 注意:如果输入可迭代对象长度不一致,按最短长度配对。需要转换为列表等容器查看结果。
错题:
#1
f1 = print(2 + 3)
f2 = lambda: print(2 + 3)
print(f1, f2, f2())
>>>5, 5, None, Function, None
#2
print("hello") and print("world")
>>>hello
"""and只对左边求值,右边被短路,返回左边的值为None,而REPL遇到None不输出内容"""
#3 神秘斐波那契数列
def mystery():
yield 1
yield 1
iter1 = mystery()
iter2 = mystery()
next(iter2)
yield from map(lambda pair: pair[0] + pair[1], zip(iter1, iter2))
series1 = mystery()
series2 = filter(lambda n: n % 2 != 0, series1)
>>>[next(series1) for _ in range(2)]
>>>[next(series1) for _ in range(2)]
>>>[next(series2) for _ in range(2)]
>>>next(series1)
>>>[1, 1]
>>>[2, 3]
>>>[5, 13]
>>>21
#4
"""抽象的猜出一个函数的作用!
存在递归调用的生成器一般是无限的!
注意赋值语句在赋值的时候是不求值的!被使用的时候才会求值"""
Functions & Control
Expression & Value
An expression describes a computation and evaluates to a value
- include : function call notation, operators and operands
- order : evaluate operater subexpression > operand ~ > apply
- types : primitive(numbers, strings, names), arithmetic, call
Programs manipulate values
- types: 4
- Expressions evaluate to values in one or more steps
Name & Assignment
Values can be assigned to names to make referring to them easier.
-
A name can only be bound to a single value.
-
One way to introduce a new name in a program is with an assignment statement.
-
Statements affect the program, but do not evaluate to values.
Function Definition
- function : abstraction
Execution rule for def Statements
- Create a function with signature
( ) - Set the body of that function to be everything indented after the first line
- Bind
to that function in the current frame
Function Call (Eval & Apply)
Procedure for calling/applying user-defined functions (for now)
- Create a new environment frame
- Bind the function’s parameters to its arguments in that frame
- Execute the body of the function in the new environment
Pure / Non-Pure Functions (Print vs Return, None)
- The special value None represents nothing in Python
- A function that does not explicitly return a value will return None
- Careful: None is not displayed by the interpreter as the value of an expression
Pure Functions just return values
Non-Pure Functions have side effects
Conditional Control (If-Statement)
Execution Rule for Conditional Statements:
Each header is considered in order
- Evaluate the header’s conditional expression if the header is not an else
- If the expression evaluates to true or the header is an else, execute the suite and skip the remaining headers
要特别注意!有的语句前不该用elif/ else, 会导致被短路,只是检查条件的话,可以只用一个if就好了
Boolean Expressions & Short Circuiting
- boolean contexts:
False values in Python: False, None, 0, ‘’ (more to come)
True values: everything else - special operators
and and and … Evaluate to the first false value. If none are false, evaluates to the last expression or or or … Evaluate to first true value. If none are true, evaluates to the last expression not Evaluates to True if if a false value and False if is a true value
返回的是操作数的值!不是True or False!
- notice: Short-Circuiting —— avoid side-effect and error
Iterative Control (While-Statement)
Execution Rule for While Statements:
- Evaluate the header’s expression
- If it is a true value, execute the (whole) suite, then return to step 1
"""斐波那契数列"""
def fib(n):
"""Compute the nth Fibonacci number, for N >= 1"""
pred, curr = 0, 1 # 0th and 1st Fibonacci numbers
k = 1 # curr is the kth Fibonacci number
while k < n:
pred, curr = curr, pred + curr
k += 1
return curr
Error Messages
Environment Diagrams
A visual tool to keep track of bindings & state of a computer program
- A frame keeps track of variable-to-value bindings.
- Every call expression has a corresponding frame.
- Global, a.k.a(又名). the global frame, is the starting frame. It doesn’t correspond to a specific call expression.
- Parent frames The parent of a function is the frame in which it was defined. If you can’t find a variable in the current frame, you check it’s parent, and so on. If you can’t find the variable, NameError
Assignment Statements
bind variable to value
Def Statements
Create a function value: in current frame

func <name>(<formal parameters>) [parent=<label>] #Its parent is the current frame.
Bind <name> to the function value in the current frame
Call Expressions

- Add a local frame, titled with the
of the function being applied. - Copy the parent of the function (not always the current frame) to the local frame: [parent=
- Bind the
to the arguments in the local frame. - Execute the body of the function in the environment that starts with the local frame
Variable Lookup
Variable Lookup:
- Lookup name in the current frame
- Lookup name in parent frame, its parent frame, etc..
- Stop at the global frame
- If not found, an error is thrown
- 注意: 函数的parent frame 取决于def的环境,而不是调用的环境
- 只能寻找并使用,若要修改则需要用nonlocal or global 声明
- 区别函数传参:若使用而未改变传入参数,则若外部参数发生变化,内部参数也会随之发生变化;若改变了该参数,相当于在内部框架对参数进行重新赋值,不会改变外部参数的值,称为闭包,并且此后外部参数再发生变化不会影响内部参数
- 求值(Evaluation):是对表达式的求解和计算过程。 应用(Apply):是指 函数调用,即将一个函数和它的参数结合在一起并执行。 当你调用一个函数时,首先会 求值 参数,接着 应用 函数。
Lambda Expressions
evaluate to functions !
Only the def statement gives the function an intrinsic name
notice: lambda for loop
如果你在循环中使用 lambda 函数时,要注意变量绑定的问题。通常,这种情况会导致 lambda 函数在创建时捕获的变量值是循环结束后的最终值。
示例:
fs = [lambda: i for i in range(5)]
print([f() for f in fs]) # [4, 4, 4, 4, 4]
原因:
lambda 捕获的是外部的 i 变量,而不是 i 在每次迭代中的值。所以,在迭代完成后,i 的值是 4,因此所有的 lambda 函数最终都返回 4。
解决方案:
fs = [(lambda i: lambda: i)(i) for i in range(5)]
print([f() for f in fs]) # [0, 1, 2, 3, 4]
通过为每个 lambda 函数创建一个局部作用域,确保每个 lambda 捕获当前 i 的值。
Recursion

- The same function fact is called multiple times, each time solving a simpler problem
- All the frames share the same parent - only difference is the argument
- What n evaluates to depends upon the current environment(在每个frame中修改n的时候实现重新赋值,而不会因为其他frame而改变当前frame的n的值)
- 执行顺序是从最末情况到base case开始创建frame,返回值是从base case得到的值回到最末层
Lists
Box and Poinger Notation

Lists in Lists in Lists
point to another list
Default Arguments
Higher order functions
takes a function as an argument value, and/or returns a function as a return value(often defined locally)
- use: Abstraction and Generalization! Give each function exactly one job, but make it apply to many related situations(分步进行的计算)
- Generalize over different form of computation
- Helps remove repetitive segments of code
Lambda Expressions vs. Def Statements
Function Design & Generalization
函数泛化,提高可读性、可维护性和扩展性,在不同情景保持复用性
Functions As Arguments
eg. 常见的高阶函数:
map():对一个可迭代对象的每个元素应用一个函数。
filter():根据给定的条件过滤一个可迭代对象。
reduce():累积地应用一个二元函数到一个可迭代对象上。
Functions That Return Functions
常常用于构建 闭包(closures),在其中返回的函数使用外部函数的环境变量来执行任务。
Function Currying
函数柯里化(Currying)是将接受多个参数的函数转化为一系列接受单一参数的函数的技术。换句话说,柯里化的过程是将一个多参数的函数分解成多个单参数的函数序列。
函数柯里化使得你可以部分应用函数参数,即先提供部分参数,得到一个新的函数,等待剩余的参数。
-
部分应用:通过柯里化,你可以将一个多参数的函数转化为一系列单一参数的函数,这有助于代码的简化和模块化。
-
函数链:柯里化可以链式调用,允许更流畅的表达式。
Self Reference
自我引用是指一个对象、函数或变量引用自己。在函数编程中,自我引用通常用于递归函数的实现,它允许函数在其内部调用自己来解决问题。
自我引用常常出现在递归中,其中函数调用自身来逐步解决子问题,直到达到一个基本情况。
Recursion
- means: solving problems with a naturally repeating structure - they are defined in terms of themselves
- It requires you to find patterns of smaller problems, and to define the smallest problem possible
Function Abstraction
- 核心:隐藏复杂性,提供简洁的接口
Definition of Recursion
递归是一种通过函数调用自身来解决问题的方法。递归的核心在于问题能被分解成更简单的子问题,且每次递归都会接近基础情况(base case)。
-
基础情况:递归停止的条件,通常是最简单的情况,比如 n == 0 或 n == 1。
-
递归情况:问题通过递归调用被分解成更小的子问题,最终汇总得到答案。
Structure of a Recursive Function
Divide - Conquer - Combine 递归函数通常遵循以下结构:
分解(Divide):将问题分解成一个或多个子问题。(多种情况or多个步骤)
解决(Conquer):解决更小的子问题,通常是递归地调用函数。
合并(Combine):将子问题的解合并,得到问题的最终解。
递归结构的关键是将一个复杂的问题通过逐步减小问题的规模,最终解决最小的子问题。
- 知道上个子问题的答案,可以怎样解决下一个问题?
- base case是什么?
Tree Recursion & Recursion Tree
树状递归是指递归函数的调用图呈树状结构。每次递归调用都会产生多个子调用,类似树的分支。递归树的深度取决于递归的层数,广度取决于每一层的调用数
Mutual Recursion
互相递归是指两个或更多的函数互相调用对方来完成任务。这种递归结构通常在两个函数的行为依赖于彼此时出现。
def is_odd(n):
if n == 0:
return False
else:
return is_even(n - 1)
def is_even(n):
if n == 0:
return True
else:
return is_odd(n - 1)
Iteration vs. Recursion
递归可以更清晰地表示分解问题的过程,尤其是当问题的结构天然适合分解时。
迭代通常更高效,因为每次递归调用都要维护栈帧,而迭代只需要更新变量。
Recursion Examples:
When learning to write recursive functions, put the base case/s first
注意考虑边界情况! eg.Count Partitions Goal: Count the number of ways to give out n ( > 0) pieces of chocolate if nobody can have more than m (> 0) pieces.
- If n is negative, then we cannot get to a valid partition
- If n is 0, then we have arrived at a valid partition
- If the largest piece we can use is 0, then we cannot get to a valid partition
Sum-Digits
def sum_digits(n):
if n == 0:
return 0
else:
return n % 10 + sum_digits(n // 10)
Cascade(级联)
级联递归指的是将多个函数或操作按顺序组合,每个函数的输出作为下一个函数的输入
#从高位开始输出每位数字
def cascade(n):
if n < 10:
print(n)
else:
cascade(n // 10)
print(n % 10)
"""在递归函数中,通常会在每一次递归调用时暂停当前函数的执行,直到递归结束并返回"""
#从低位开始输出每位数字
def cascade(n):
if n < 10:
print(n)
else:
print(n % 10)
cascade(n // 10)
Fibonacci
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
Count-Partitions
def count_partitions(n):
if n == 0:
return 1
count = 0
for i in range(1, n + 1):
count += count_partitions(n - i)
return count
If Time - Speeding Up Recursion
store some values that had already calculated
SUMMERY
Recursion has three main components
- Base case/s: The simplest form of the problem
- Recursive call/s: Smaller version of the problem
- Use the solution to the smaller version of the problem to arrive at the solution to the original problem
- When working with recursion, use functional abstraction: assume the recursive call gives the correct result
- Tree recursion makes multiple recursive calls and explores different choices
- Use doctests and your own examples to help you figure out the simplest forms and how to make the problem smaller
Sequence
Sequence Abstraction
All sequences have finite length. Each element in a sequence has a discrete integer index.
Sequence Operation (Expression)
#### Get Item
get the ith element <seq>[i]
#### Slicing
##### Slicing Creates New Values
create a copy of the sequence from i to j <seq>[i:j:skip]
#### Check Membership
check if the value of <expr> is in <seq> <expr> in <seq>
#### Concatenate
combine two sequences into a single sequence <s1> + <s2>
Sequence Processing (Statement)
#### Iterating
For each element in <seq>:
1) Bind it to <name>
2) Execute <body>
Unpacking

Range
range(<start>, <end>, <skip>)

List
List Comprehensions
[<expr> for <name> in <seq> if <cond>]
String
String Literals (Three Forms)
Python 中有三种常见的字符串字面量表示形式:
单引号: ‘Hello’
双引号: “Hello”
三引号: ‘‘‘Hello’’’ 或 “““Hello””” 三引号用于表示多行字符串,或者在字符串中包含引号时可以避免转义。
Sequence Aggregation (Sum, Max / Min, All / Any)

Dictionary

Data abstraction and Tree
Compound values combine other values together Data abstraction lets us manipulate compound values as units e.g. 有理数由分子(numerator)和分母(denominator)组成,是分数的精确表示形式,以整数对形式存在。常规表达进行除法运算,可能会丢失精确表示。 so:
# 基础构造与选择器
def rational(n, d): return [n, d] # 构造有理数(列表形式)
def numer(x): return x[0] # 获取分子
def denom(x): return x[1] # 获取分母
# 运算实现
def mul_rational(x, y): return rational(numer(x)*numer(y), denom(x)*denom(y)) # 乘法
def add_rational(x, y):
nx, dx = numer(x), denom(x)
ny, dy = numer(y), denom(y)
return rational(nx*dy + ny*dx, dx*dy) # 加法
def print_rational(x): print(numer(x), '/', denom(x)) # 打印
def rationals_are_equal(x, y): return numer(x)*denom(y) == numer(y)*denom(x) # 相等判断
# 最简形式优化(引入最大公约数)
from math import gcd
def rational(n, d):
g = gcd(n, d)
return [n//g, d//g] # 返回最简分数形式的有理
Abstraction Barrier (between Representation & Use)
-
def: 将数据的表示和使用分开的一种设计思想。
-
通过抽象屏障,可以使得数据的表示方式对外部用户隐藏,从而保护数据的结构,使其不容易受到外部代码的直接修改。 把底层的数据结构和功能封装起来,使得使用者可以通过抽象接口来操作数据,而不需要理解内部的实现。
-
表示:指的是数据的内部结构,如何在计算机中存储这些数据。
-
使用:指的是外部代码与数据进行交互的方式,用户与数据的交互不关心数据的具体实现,只关心如何操作数据。
Constructor & Selectors
-
Constructor(构造函数)是用于创建数据对象的函数,它定义了如何通过一些基本组件来构造出复杂的数据结构。
-
Selectors(选择器)是用于从已构建的数据结构中提取信息的函数。它们允许用户访问数据结构中的元素或属性。
-
break: 过度依赖数据的内部实现,或者在外部代码中直接修改、访问类和数据结构的私有部分或底层细节
Abstract Data Type
指数据结构和操作的组合 e.g.
-
列表(List):一个抽象数据类型,可以进行元素添加、删除、查找等操作,具体实现可以是动态数组、链表等。
-
栈(Stack):一种抽象数据类型,具有push()和pop()等操作,可以通过不同的实现方式(例如数组或链表)来实现。
Closure
指函数不仅能够访问其自己的局部变量,还能够访问外部函数的局部变量。闭包使得函数能够“记住”它被创建时的环境,即使外部函数已经返回,内部函数仍然能够访问外部函数的变量。
Tree Abstraction
- 递归描述(木质树视角)
树由一个根(root)和一组分支(branches)组成。
每个分支本身也是一棵树。
零个分支的树称为叶子(leaf)。
- Label:节点的值
- Branches:子节点或子树,是一个列表,其中每个元素表示一个子节点。
- 相对描述(家族树视角) 树中每个位置称为节点(node)。 每个节点有一个标签值(label value)。 节点间存在父子关系(parent/child)。 常根据节点位置描述值的关系
树包含标签值和分支列表的结构,分支列表可为空(对应叶子)
Tree Implementation
Constructor: tree
def tree(label, branches=[]):
for branch in branches:
assert is_tree(branch)
return [label] + list(branches)
Selectors: label, branches
def label(tree):
return tree[0]
def branches(tree):
return tree[1:]
Convinence Functions: is_tree, is_leaf
def is_tree(tree):
if type(tree) != list or len(tree) < 1:
return False
for branch in branches(tree):
if not is_tree(branch):
return False
return True
def is_leaf(tree):
return not branches(tree)
Tree Processing (using Recursion)
def count_leaves(t):
"""统计树的叶子节点总数"""
if is_leaf(t):
return 1 # 叶子节点返回1
else:
# 递归计算每个分支的叶子数,求和聚合
branch_counts = [count_leaves(b) for b in branches(t)]
return sum(branch_counts)
def increment_leaves(t):
"""返回叶子节点标签递增1的新树"""
if is_leaf(t):
return tree(label(t) + 1) # 叶子节点标签+1
else:
# 递归处理每个分支,保留非叶子节点标签
bs = [increment_leaves(b) for b in branches(t)]
return tree(label(t), bs)
def increment(t):
"""返回所有节点标签递增1的新树"""
# 根节点标签+1,递归处理所有分支
return tree(label(t) + 1, [increment(b) for b in branches(t)])
Mutablility
指对象在创建后是否可以改变其内容或状态,它们影响了对象的行为、存储方式、以及如何处理对象的引用
Objects
对象 是程序中所有数据的基本单位。每个对象都有两个主要属性:身份(Identity)和 值(Value)。对象的身份由它的内存地址决定,而值则是对象所包含的数据。对象的行为通过其类型(如列表、字典、整数等)决定
Mutable Objects
Lists
append():向列表末尾添加元素。
pop():移除并返回列表的最后一个元素,或指定索引的元素。
remove():移除列表中第一个匹配的指定元素。
extend():将一个可迭代对象中的元素添加到列表末尾。
Slice Assignment:通过切片语法修改列表中的部分元素。
Dictionaries
由键(key)和值(value)对组成的映射。字典允许通过键来查找对应的值,可以进行修改、删除或添加新的键值对。
Mutate with Functions
由于 Python 中的可变对象是通过引用传递的,修改可变对象会影响原始对象
Immutable Objects
Tuple, str, int, float 可变仅指元组本身的元素绑定不可变,若元组包含可变元素(如列表),该可变元素的内容可被修改。
Identity & Equality
- Identity 指的是对象在内存中的位置,通常由 id() 函数来查看。 同一性(is运算符):判断两个表达式是否指向同一个对象。
- Equality 则是指对象的内容是否相等。对于可变对象,修改对象的内容不会改变对象的身份;而对于不可变对象,任何修改都需要创建一个新对象。 同一对象必然值相等,但值相等的对象不一定是同一对象。 相等性(==运算符):判断两个表达式的值是否相等。
Default Arguments
函数的默认参数值是函数定义的一部分,仅在函数定义时创建一次,而非每次调用时重新生成。 若默认参数是可变对象(如列表),多次调用函数会共享该对象,导致意外的状态累积。
def append_to_list(value, lst=[]): # 注意:默认值是可变的
lst.append(value)
return lst
print(append_to_list(1)) # 输出 [1]
print(append_to_list(2)) # 输出 [1, 2],因为默认的列表是共享的
要避免这种情况,使用不可变的默认参数(例如 None)并在函数体内初始化:
def append_to_list(value, lst=None):
if lst is None:
lst = []
lst.append(value)
return lst
print(append_to_list(1)) # 输出 [1]
print(append_to_list(2)) # 输出 [2],避免了共享同一个列表的问题
Mutable Functions (Nonlocal)
- nonlocal 关键字用于修改外层函数作用域中的变量,而不是创建一个局部变量
- tip:
-
如果外层函数的变量是可变的(例如列表、字典等),你可以修改它的内容,而无需使用 nonlocal。但如果你想直接对变量本身(例如赋值为一个新对象)进行修改,就需要使用 nonlocal。
-
如果外层函数的变量是不可变的(例如整数、字符串、元组等),则即使使用 nonlocal 关键字,你也只能修改它的引用,而不能直接改变它的值(因为不可变对象的值一旦创建就无法修改)。
-
UnboundLocalError
当你在函数内部引用一个变量,并且该变量在局部作用域中没有声明时,Python 会抛出 UnboundLocalError 错误。这通常发生在试图在函数中修改局部变量时,而没有明确声明该变量为 nonlocal 或 global。
Mutate Mutable Values wihout Nonlocal
如果一个变量是可变的且位于外部作用域,你可以在内部函数中修改它的内容,而不必使用 nonlocal
#嵌套列表
t = [1, 2, 3]
t[1:3] = [t]
t.extend(t)
t = [[1, 2], [3, 4]]
t[0].append(t[1:2])
Iterable
Iterable 是任何能够逐个返回其成员的对象。它包括序列类型(如列表、字符串、元组)以及非序列类型(如字典)。
Iterators (iter & next)
- iter(iterable):此函数返回一个迭代器(iterator),它可以逐个遍历可迭代对象(iterable)的元素。可以把它理解为一本书的书签,从书的前面开始。
- next(iterator):此函数返回迭代器的下一个元素,并将书签移动到下一页。迭代器会记住它的当前位置。如果当前位置已经是书的结尾,它会抛出一个错误。
Views of a Dictionary (keys & values)
字典及其键(keys)、值(values)和项(items)都是可迭代对象。
d.keys():返回字典键的可迭代视图。d.values():返回字典值的可迭代视图。d.items():返回字典键值对的可迭代视图。
Built-in Functions for Iteration
- map(func, iterable):对可迭代对象中的每个元素应用指定的函数。
- filter(func, iterable):过滤可迭代对象中的元素,保留通过函数判断为真的元素。
- zip(first_iter, second_iter):将两个或多个可迭代对象中的元素按顺序组成元组返回。
- reversed(sequence):返回一个反向迭代器,逐个遍历序列中的元素。
Create Iterators
这些函数允许我们创建迭代器:
- map:将一个函数应用到每个元素。
- filter:根据一个条件函数筛选元素。
- zip:将两个可迭代对象的元素按顺序组成元组。
- reversed:创建一个反向遍历序列的迭代器。
View Iterators
这些函数将可迭代对象转换为不同的数据结构视图:
- list(iterable):将可迭代对象转换为列表。
- tuple(iterable):将可迭代对象转换为元组。
- sorted(iterable):将可迭代对象排序后返回一个新的列表。
# Example of iterators and map function
def naturals():
x = 0
while True:
yield x
x += 1
>>> nats = naturals()
>>> next(nats)
0
>>> next(nats)
1
>>> nats1, nats2 = naturals(), naturals()
>>> [next(nats1) * next(nats2) for _ in range(5)]
[0, 1, 4, 9, 16] # Squares the first 5 natural numbers
Generators and Generator Functions
- Generators:生成器是通过调用生成器函数自动创建的迭代器。它使用
yield关键字逐个返回值,并保持函数的状态,以便后续调用next()时继续执行。 - Generator Functions:生成器函数包含一个或多个
yield语句。与普通函数不同,生成器函数在每次yield时返回一个值,而不是一次性返回。
yield value
yield关键字用于生成一个值,并挂起函数的执行,之后可以继续执行。
yield from iterator / iterable
yield from语法用于将另一个迭代器或可迭代对象的所有值逐个返回。它简化了代码,在需要将值传递给另一个生成器或可迭代对象时非常有用。
Generators Representing Infinite Sequences
生成器可以表示无限序列。例如,上述的naturals()函数生成一个从0开始的无限自然数序列。
Exception
Exception 是程序在运行时遇到的错误,会打断程序的正常执行流程。
| 中文名 | 英文名 | 说明 |
|---|---|---|
| 异常 | Exception | 程序运行时检测到的错误,会中断当前代码的正常执行流程。 |
| 抛出异常 | Raise an Exception | 使用 raise 语句主动触发异常。 |
| 捕获异常 | Catch / Handle Exception | 使用 try...except 捕获并处理异常,防止程序崩溃。 |
| 异常传播 | Exception Propagation | 如果当前作用域未捕获异常,异常会沿着调用栈往上层传递,直到被捕获或导致程序终止。 |
| 异常对象 | Exception Object | 每个异常都是一个类的实例,包含错误类型、信息、堆栈信息等。 |
| 异常类层级结构 | Exception Hierarchy | Python 中所有异常都是从 BaseException 类继承而来的一棵树。 |
Built-in Exceptions
Python提供了多个内建异常,每种异常用于不同类型的错误。
| 异常名 | 英文说明 | 常见触发场景 |
|---|---|---|
SyntaxError |
Syntax error | 语法错误(代码结构非法) |
IndentationError |
Indentation error | 缩进错误 |
NameError |
Undefined variable | 使用了未定义的变量 |
TypeError |
Type mismatch | 类型不兼容(如把字符串加到整数上) |
ValueError |
Invalid value | 值类型正确但取值非法(如 int("abc")) |
IndexError |
Index out of range | 列表索引越界 |
KeyError |
Missing dictionary key | 字典中查找不存在的键 |
AttributeError |
Missing attribute | 对象没有该属性或方法 |
ZeroDivisionError |
Division by zero | 除以零错误 |
IOError / OSError |
Input/Output error | 文件或系统操作失败(如找不到文件) |
FileNotFoundError |
File not found | 打开不存在的文件 |
ImportError / ModuleNotFoundError |
Import error | 模块导入失败 |
RuntimeError |
Generic runtime error | 运行时检测到其他异常情况 |
RecursionError |
Maximum recursion depth exceeded | 递归层数太深 |
AssertionError |
Assertion failed | 断言失败(assert) |
StopIteration |
Iterator finished | 迭代器结束(由迭代协议内部抛出) |
StopAsyncIteration |
Async iterator finished | 异步迭代结束 |
Exception Mechanism
- Detection (发现错误): 错误会在程序执行时被发现。
- Raising (raise): 可以通过
raise关键字显式地抛出错误。 - Propagation (异常传播): 如果当前作用域没有捕获异常,异常会沿着调用栈向上传播,直到被捕获或导致程序终止。
- Catching (捕获): 错误可以使用
try/except块进行捕获。 - Else (无异常时执行): 如果
try块没有抛出异常,则执行else块。 - Finally (始终执行): 无论是否发生异常,
finally块总是会执行。
raise Syntax
raise <expression> # <expression> 必须是一个异常对象
Example
def divide(a, b):
try:
assert b != 0, "Divider cannot be zero"
result = a / b
except AssertionError as e:
print("Assertion failed:", e)
except ZeroDivisionError as e:
print("Caught division error:", e)
else:
print("Result is:", result)
finally:
print("Computation ended.")
divide(10, 0)
Output:
Assertion failed: Divider cannot be zero
Computation ended.