一. yield
— 生成器
基本概念:
yield
是 Python 中的一个关键字,它用于定义生成器。生成器是一种特殊类型的迭代器,它与常规函数的不同之处在于,生成器函数在执行时会“暂停”并返回一个值,之后可以恢复执行,直到再次遇到 yield
。
与普通函数不同,生成器在被调用时并不会立即执行代码。相反,它会返回一个生成器对象,这个对象可以用于逐个生成值,直到所有值都被生成完为止。
生成器函数:
生成器函数是通过 def
定义的普通函数,但它包含一个或多个 yield
表达式。当生成器函数被调用时,它不会立即执行,而是返回一个迭代器对象(即生成器)。调用该生成器的 __next__()
方法(可以通过 next()
函数调用)时,函数会开始执行,直到遇到 yield
语句时暂停,返回值给调用者,并保留当前函数的状态。
工作原理:
- 暂停与恢复:每次遇到
yield
时,函数的执行状态被“冻结”,函数返回yield
后的值,直到下次调用next()
时,函数从上次“冻结”的位置恢复执行。 - 一次生成一个值:生成器每次生成一个值,它并不会一次性返回所有的值。因此,生成器在处理大量数据时非常高效,因为它不会一次性将所有的值存储在内存中。
示例:
# 生成器函数:生成从1到最大值的数字
def count_up_to(max):
count = 1
while count <= max:
yield count # 每次暂停并返回当前的count
count += 1
# 创建生成器对象
counter = count_up_to(3)
# 逐个获取生成的值
print(next(counter)) # 输出: 1
print(next(counter)) # 输出: 2
print(next(counter)) # 输出: 3
生成器的特点:
- 节省内存:生成器一次只生成一个值,避免了在内存中存储所有值,适合处理大量数据。
- 惰性求值:生成器是惰性计算的,它们直到需要时才生成下一个值。
- 状态保存:每次执行到
yield
时,函数的执行状态都会被保存,下一次next()
调用时会从上次的yield
语句继续执行。
迭代器协议:
生成器实现了迭代器协议,即支持 __iter__()
和 __next__()
方法,因此可以用 for
循环进行遍历:
for num in count_up_to(5):
print(num)
这将逐个输出从 1 到 5 的数字。
二. raise
— 异常抛出
基本概念:
raise
关键字用于在程序中主动抛出异常。它可以用来抛出标准异常,也可以抛出自定义的异常。使用 raise
可以让你显式地触发错误,或者将错误信息传递给上层调用者。它还可以与 try...except
块结合使用,用于重新抛出已捕获的异常。
抛出异常:
raise
后面可以跟一个异常类(如 ValueError
、TypeError
等)或者异常实例。你可以使用 raise
主动触发异常,在程序的错误处理部分或在输入数据无效时使用。
语法:
raise ExceptionType("Error message")
- ExceptionType:是抛出的异常类型,可以是 Python 内建的异常(如
ValueError
,TypeError
,IndexError
等),也可以是自定义的异常类。 - Error message:是一个可选的错误信息,用于描述异常的具体内容。
示例:
def divide(a, b):
if b == 0:
raise ZeroDivisionError("Cannot divide by zero!") # 主动抛出异常
return a / b
try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(f"Error occurred: {e}") # 捕获异常并处理
这段代码演示了如何使用 raise
触发一个 ZeroDivisionError
异常。
重新抛出异常:
当你在 try
块中捕获异常后,可能需要进一步处理或记录信息,也可能希望将该异常传递给调用者。在这种情况下,你可以使用 raise
重新抛出异常。
try:
# 可能会抛出异常的代码
raise ValueError("An error occurred")
except ValueError as e:
print(f"Caught exception: {e}")
raise # 重新抛出异常
在这个例子中,捕获并处理了异常后,使用 raise
将异常重新抛出,可以让调用者在更高的层次上处理这个异常。
自定义异常:
你也可以自定义异常类,继承自 Exception
类,然后使用 raise
抛出自定义异常。
class CustomError(Exception):
def __init__(self, message):
super().__init__(message)
def do_something():
raise CustomError("This is a custom error")
try:
do_something()
except CustomError as e:
print(f"Caught custom error: {e}")
这里,我们定义了一个 CustomError
类,它继承自 Exception
。在 do_something()
函数中,我们使用 raise
抛出了一个自定义的异常。
raise
的应用场景:
- 输入验证:当输入数据不符合预期时,可以使用
raise
抛出异常,提示用户修正错误。 - 错误传播:在处理过程中遇到无法解决的错误时,使用
raise
重新抛出异常,交给上层逻辑来处理。 - 异常链:有时候你需要捕获某个异常并将其包装成不同类型的异常,以提供更多上下文信息或不同的错误处理方式。
总结:
-
yield
:- 使函数成为生成器,返回一个迭代器。
- 节省内存,因为生成器按需生成结果,而不是一次性生成所有值。
- 支持惰性求值,直到调用
next()
时才执行。 - 常用于处理大量数据或者需要按需生成数据的场景。
-
raise
:- 用于主动抛出异常。
- 可以触发标准异常或自定义异常,提供错误信息。
- 与
try...except
结合使用时,可以捕获、处理、或重新抛出异常,控制错误流转。
这两个关键字在 Python 中各自承担不同的角色,yield
主要用于迭代和数据流控制,而 raise
主要用于异常处理和错误控制。