迭代器与生成器
1. 迭代器
迭代器是实现了迭代器协议的对象,这一协议包含 __iter__() 和 __next__() 方法。__iter__() 方法返回迭代器对象本身,而 __next__() 方法返回迭代器的下一个值。若没有更多值,就会抛出 StopIteration 异常。
Python 有许多内置函数可以返回迭代器,例如 range()、enumerate()、zip() 等。
要创建一个迭代器,需要定义一个类,该类包含 __iter__() 和 __next__() 方法。
示例代码
# 自定义迭代器类
class MyRange:
def __init__(self, start, end):
self.start = start
self.end = end
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current < self.end:
value = self.current
self.current += 1
return value
else:
raise StopIteration
# 使用自定义迭代器
my_range = MyRange(0, 5)
for num in my_range:
print(num)
代码解释
__init__方法:对迭代器的起始值、结束值和当前值进行初始化。__iter__方法:返回迭代器对象本身。__next__方法:返回迭代器的下一个值,若没有更多值,就抛出StopIteration异常。- 使用
for循环遍历迭代器:for循环会自动调用迭代器的__iter__()和__next__()方法。
1.iter() 方法
iter() 是 Python 的内置函数,其作用是把一个可迭代对象(像列表、元组、字符串等)转变为迭代器对象。迭代器是一种支持 __next__() 方法的对象,借助该方法可以逐个访问可迭代对象里的元素。
语法
iter(object[, sentinel])
object:这是要转换为迭代器的可迭代对象。sentinel(可选):若提供了sentinel参数,object就必须是可调用对象(如函数),此时iter()会创建一个迭代器,每次调用迭代器的__next__()方法时,都会调用object,直到返回值等于sentinel为止。
示例
my_list = [1, 2, 3, 4, 5]
my_iterator = iter(my_list)
print(type(my_iterator)) # 输出: <class 'list_iterator'>
2.next() 方法
next() 同样是 Python 的内置函数,它用于从迭代器中获取下一个元素。当迭代器中没有更多元素时,会抛出 StopIteration 异常。
语法
next(iterator[, default])
iterator:这是要从中获取元素的迭代器对象。default(可选):若提供了default参数,当迭代器耗尽时,会返回default值,而不会抛出StopIteration异常。
示例
my_list = [1, 2, 3, 4, 5]
my_iterator = iter(my_list)
try:
print(next(my_iterator)) # 输出: 1
print(next(my_iterator)) # 输出: 2
print(next(my_iterator)) # 输出: 3
print(next(my_iterator)) # 输出: 4
print(next(my_iterator)) # 输出: 5
print(next(my_iterator)) # 抛出 StopIteration 异常
except StopIteration:
print("迭代器已耗尽")
结合使用
iter() 和 next() 方法通常结合使用,以实现对可迭代对象的手动迭代。下面是一个完整的示例:
my_list = [1, 2, 3, 4, 5]
my_iterator = iter(my_list)
while True:
try:
element = next(my_iterator)
print(element)
except StopIteration:
break
在这个示例中,先使用 iter() 函数把列表转换为迭代器,然后利用 next() 函数逐个获取迭代器中的元素,直至迭代器耗尽,捕获 StopIteration 异常并终止循环。
迭代器的优势
- 节省内存:迭代器在需要时才生成元素,无需一次性将所有元素存储在内存中。
- 惰性求值:只有在调用
__next__()方法时才会计算下一个元素,这在处理大规模数据时非常有用。
迭代器的应用场景
- 循环遍历:迭代器最常见的用途是在
for循环中遍历元素。 - 数据处理:在处理大规模数据时,可以使用迭代器逐个处理元素,避免内存溢出。
2.生成器
在 Python 里,生成器是一种特殊的迭代器,能够一边迭代一边生成元素,而不用事先把所有元素都存储在内存里。这种特性让生成器在处理大规模数据或者无穷序列时极为高效。下面会详细介绍 Python 生成器的定义、使用、工作原理等内容。
在 Python 中,有两种方式可以定义生成器:生成器函数和生成器表达式。
1. 生成器函数
生成器函数是带有 yield 关键字的普通函数。当函数被调用时,它不会立即执行,而是返回一个生成器对象。每次调用生成器的 __next__() 方法(在 Python 中也可以使用 next() 函数),函数会执行到下一个 yield 语句处,返回 yield 后面的值,并暂停执行。下一次调用 __next__() 方法时,函数会从上次暂停的地方继续执行。
def my_generator():
yield 1
yield 2
yield 3
# 创建生成器对象
gen = my_generator()
# 使用 next() 函数获取生成器的下一个值
print(next(gen)) # 输出 1
print(next(gen)) # 输出 2
print(next(gen)) # 输出 3
# 当生成器没有更多值时,会抛出 StopIteration 异常
try:
print(next(gen))
except StopIteration:
print("生成器已耗尽")
2. 生成器表达式
生成器表达式类似于列表推导式,不过它使用圆括号而不是方括号。生成器表达式返回一个生成器对象,它会逐个生成元素,而不是一次性生成所有元素。
# 生成器表达式
gen_expr = (x * 2 for x in range(5))
# 遍历生成器表达式
for num in gen_expr:
print(num)
3. 生成器的工作原理
生成器的工作原理基于 Python 的迭代协议。生成器对象实现了 __iter__() 和 __next__() 方法,因此可以被迭代。当调用 next() 函数时,生成器会执行到下一个 yield 语句,返回 yield 后面的值,并暂停执行。当再次调用 next() 函数时,生成器会从上次暂停的地方继续执行,直到遇到下一个 yield 语句或者函数结束。
生成器的优点
- 节省内存:生成器逐个生成元素,不需要一次性存储所有元素,因此在处理大规模数据时可以显著节省内存。
- 惰性求值:生成器只有在需要时才会生成元素,这种惰性求值的特性可以提高程序的效率。
生成器的应用场景
- 处理大规模数据:当处理大规模数据集时,使用生成器可以避免将所有数据加载到内存中,从而节省内存。
- 无穷序列:生成器可以用来表示无穷序列,例如斐波那契数列。
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 创建斐波那契数列生成器
fib_gen = fibonacci()
# 输出前 10 个斐波那契数
for _ in range(10):
print(next(fib_gen))
4. 生成器的方法
除了 __next__() 方法外,生成器还提供了一些其他的方法,例如 send()、throw() 和 close()。
send(value):用于向生成器发送一个值,并继续执行生成器。throw(type[, value[, traceback]]):用于在生成器内部抛出一个异常。close():用于关闭生成器,停止生成元素。
def counter():
num = 0
while True:
received = yield num
if received is not None:
num = received
else:
num += 1
# 创建生成器对象
gen = counter()
# 获取第一个值
print(next(gen)) # 输出 0
# 发送一个值给生成器
print(gen.send(10)) # 输出 10
# 继续获取下一个值
print(next(gen)) # 输出 11
# 关闭生成器
gen.close()
# 尝试获取下一个值,会抛出 StopIteration 异常
try:
print(next(gen))
except StopIteration:
print("生成器已关闭")
发表评论