迭代器和生成器

本文最后更新于:2024年5月18日 下午

应用场景:处理大量数据或需要节省内存

迭代器

迭代器是遵循迭代器协议的对象,即它们实现了__iter__()__next__()两个方法。__iter__()返回迭代器对象本身,__next__()返回序列的下一个元素。如果没有更多元素可返回,则抛出StopIteration异常。

iter( )方法:

在Python中,iter()函数用于获取一个迭代器。它通常与集合数据类型(如列表、元组、字典、集合等)一起使用,以获取这些数据类型的迭代器对象。
以下是iter()函数的基本用法:

  1. 无参数调用iter()可以不带参数调用,这时它会返回一个空的迭代器。

    1
    iter()
  2. 调用具有__iter__()方法的对象:如果对象实现了__iter__()方法,iter()函数将调用这个方法来获取迭代器。

    1
    my_iter = iter(my_object)
  3. 调用具有__getitem__()方法的对象:如果对象实现了__getitem__()方法,iter()函数会创建一个迭代器,尝试按顺序获取对象的元素。

    1
    my_iter = iter(my_object)
  4. 调用带有两个参数iter()也可以接受两个参数,其中第一个参数是函数,第二个参数是哨符(哨符(sentinel)是一个特殊的值,用于在迭代过程中指示何时停止。通常,哨符与iter()函数的第二个参数一起使用,以创建一个迭代器,该迭代器重复调用函数直到它返回哨符值。这种用法允许创建一个基于某些条件停止的迭代器,而不是基于固定数量的元素或项。哨符通常用于文件处理,其中哨符可以是一个特殊的行或字符,指示文件的结束)。这种用法用于创建一个迭代器,该迭代器重复调用函数直到它返回哨符值。

    1
    my_iter = iter(function, sentinel)

示例:

展示如何使用iter()函数获取列表的迭代器

1
2
3
4
5
6
7
8
9
10
11
12
# 创建一个列表

my_list = [1, 2, 3, 4, 5]

# 使用iter()获取列表的迭代器

my_iter = iter(my_list)

# 使用next()获取迭代器的下一个元素

next_element = next(my_iter)
next_element

使用iter()函数获取列表[1, 2, 3, 4, 5]的迭代器后,我们使用next()函数获取迭代器的下一个元素,得到的结果是1
也可以使用next()函数连续调用迭代器来遍历列表中的所有元素。当没有更多元素可返回时,next()函数会抛出StopIteration异常。

next()方法:

next()函数用于获取迭代器的下一个元素。当你有一个迭代器对象时,你可以使用next()函数来逐个访问其元素,直到没有更多元素可访问,此时next()会抛出StopIteration异常。

迭代器是一个实现了__next__()方法的对象,该方法返回序列的下一个元素。next()函数简单地调用迭代器的__next__()方法。

迭代器用于遍历集合,如列表、元组、字典和集合等,而不需要在内存中存储整个集合。这意味着迭代器在处理大数据集时非常高效。

迭代器示例

用于创建一个可以生成斐波那契数列的迭代器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class FibonacciIterator:
def __init__(self, n):
self.n = n
self.a, self.b = 0, 1
self.count = 0

def __iter__(self):
return self

def __next__(self):
if self.count < self.n:
value = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return value
else:
raise StopIteration

# 使用迭代器生成前10个斐波那契数
fib_iter = FibonacciIterator(10)
list(fib_iter)

在这个迭代器中:

  • __init__ 方法初始化迭代器,设置要生成的斐波那契数的数量(n),并初始化两个变量 ab,分别代表数列中的前两个数。
  • __iter__ 方法返回迭代器对象本身。
  • __next__ 方法计算下一个斐波那契数,并更新 ab 的值。如果生成的斐波那契数的数量达到了指定的数量(n),则抛出 StopIteration 异常。

生成器

在Python中,生成器是一种特殊类型的迭代器,它使用yield语句来生产一系列的值,用于迭代。生成器的优势在于它提供了惰性求值(lazy evaluation),这意味着它只在需要时生成值,而不是一次性生成所有值。这样做可以节省内存并提高效率。
生成器函数的定义与普通函数类似,但有以下特点:

  • 包含yield关键字。
  • 当函数被调用时,它返回一个生成器对象,但不会立即执行函数体。
  • 当生成器对象的__next__()方法被调用时,函数体开始执行,直到遇到yield语句,此时它返回yield后的值并暂停执行。

下次调用__next__()时,函数从上次暂停的地方继续执行,直到再次遇到yield或函数结束。

yield关键字

yield VS return

  • return作为结尾的普通函数直接返回所有结果,程序终止不再运行,并销毁局部变量;
  • yield产生一个断点,暂停函数,挂起函数,保存当前状态。并且在yield处返回某个值,返回之后程序就不再往下运行了。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def fun_yield():
print("starting fun yield")
while True:
res = yield 4
print("判断yield之后是否继续执行",res)

g = fun_yield() # 调用这个函数只是会得到一个生成器
print("函数结果是一个生成器:",g)

print("对此生成器还是进行调用:")
print("第一次调用")
print("生成器的返回值",next(g))
print("第二次调用")
print("生成器的返回值",next(g))
print("第三次调用")
print("生成器的返回值",next(g))

结果:

示例

  • 程序开始执行以后,因为fun_yield中有yield关键字,所以函数并不会真的执行,而是先得到一个实例化的生成器对象,结果1可以看出不会真的运行。

  • 直到调用next(),fun_yield正式开始执行,先执行函数中的print(“starting fun yield”),然后进入while循环,结果2可以看出

  • 程序遇到yield关键字,然后把yield理解为return,return了一个4之后,程序停止,并没有执行后面的print(“判断yield之后是否继续执行”,res)操作,此时next(g)语句执行完成,所以输出的前两行,接下来准备运行第二次调用,结果3可以看出

  • 执行下面的print(“生成器的返回值”,next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从上一次yield停止的断点开始执行,也就是要执行print(“判断yield之后是否继续执行”,res)操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数,也就相当于说res里面是没有内容的),所以这个时候res赋值是None,所以接着下面的输出就是None,结果4可以看出

  • 之后的程序会继续在while里执行,又一次碰到yield,这个时候同样return出4,结果5可以看出

生成器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def fibonacci_generator(n):
"""生成斐波那契数列的生成器函数"""
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b

#创建一个生成器对象,用于生成前10个斐波那契数

fib_gen = fibonacci_generator(10)

#使用next()获取生成器的下一个元素

fibonacci_sequence = [next(fib_gen) for _ in range(10)]
fibonacci_sequence
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

使用生成器函数fibonacci_generator,我们成功地生成了前10个斐波那契数:0, 1, 1, 2, 3, 5, 8, 13, 21, 34。
这个示例展示了生成器如何简化生成序列的过程,同时节省内存。由于生成器在迭代时只生成当前需要的值,因此它们在处理大数据集或无限序列时特别有用。


迭代器和生成器
https://forever0823.github.io/2024/04/27/迭代器和生成器/
作者
Alan
发布于
2024年4月27日
许可协议