Python生成器

生成器 Generators

要理解生成器,首先要理解迭代器,迭代器由以下三个部分组成:

  • 可迭代对象(iterable)
  • 迭代器(iterator)
  • 迭代(iteration)

1. 可迭代对象

只要定义了可以返回一个迭代器的__iter__方法的对象,或者定义了可以支持下标索引的__getitem__方法。

2. 迭代器

只要定义了一个__next__方法的对象。

3. 迭代

从某个地⽅(⽐如⼀个列表)取出⼀个元素的过程。当我们使⽤⼀个循环来遍历某个东西时,这就叫⼀个迭代。


生成器

生成器也是一种迭代器,但是只能对其迭代一次。因为生成器没有把所有的值存在内存中,而是在运行时生成值(lazy)。可以通过遍历来使用它们,使用for循环或传递给任意可以进行迭代的函数和结构。

大多数生成器是以函数来实现的,它们并不返回一个值,而是yield一个值。

生成器最好的使用场景是:不需要一次将所有计算的结果全部放在内存中,或者一次读出全部的原始数据,这样会造成很大的内存占用。

上述例子中,我们用for循环来迭代这个生成器函数,同样,我们可以使用python的next()函数来获得迭代器的下一个元素。

基础数据类型list和str都是可迭代对象,我们经常会使用for循环来获得每一个下标的数据。

但是我们发现,不能调用next来迭代字符串和数据,这是因为:

数组和字符串是可迭代对象,但不是迭代器。

使用for关键字迭代可迭代对象时,for会自动获取到迭代器,进行迭代。但是next只能对迭代器进行迭代,因此我们需要使用到iter()函数来根据一个可迭代对象返回一个迭代器对象!

同时,next方法没有实现对StopIteration异常的捕捉,在自己写的迭代器内一般需要额外进行迭代结束的编写。


测验:

现在我们要写一个生成器,迭代一个数组的时候,不再是按顺序取出而是隔一个元素取出。

1. 函数式迭代器(yield)

python 复制代码
def skip_elements(arr):
    for i in range(0, len(arr), 2):  # 从索引0开始,每次跨越2个元素
        yield arr[i]

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
gen = skip_elements(my_list)

for element in gen:
    print(element)

2. 编写一个迭代器:

python 复制代码
class SkipElementsGenerator:
    def __init__(self, arr):
        self.arr = arr
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.arr):
            raise StopIteration
        element = self.arr[self.index]
        self.index += 2  # 每次跳过一个元素
        return element

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
gen = SkipElementsGenerator(my_list)

for element in gen:
    print(element)

SkipElementsGenerator是一个迭代器,我们直接将要迭代的数组当成形参丢入以初始化迭代器。

注意:因为要用for迭代迭代器,因此迭代器也必须得是可迭代对象,这里只需要简单实现__iter__函数并且返回self即可!

Another Way

比较直观了解可迭代对象与迭代器的例子:

python 复制代码
class MyArray:
    def __init__(self, arr):
        self.arr = arr

    def __iter__(self):
        return SkipElementsGenerator(self.arr)


class SkipElementsGenerator:
    def __init__(self, arr):
        self.arr = arr
        self.index = 0

    def __next__(self):
        if self.index >= len(self.arr):
            raise StopIteration
        element = self.arr[self.index]
        self.index += 2  # 每次跳过一个元素
        return element

# 创建自定义数组对象
my_list = MyArray([1, 2, 3, 4, 5, 6, 7, 8, 9])

# 使用生成器迭代元素
for element in my_list:
    print(element)

原始数据是可迭代对象,实现__iter__方法,返回它的迭代器。

迭代器实现__next__方法,迭代返回每一次迭代的值。

这个时候for循环直接迭代可迭代对象,不像上一个例子一样是迭代迭代器。

for循环是先找到可迭代对象的迭代器! 通过__iter__方法!

然后对迭代器进行迭代! 通过迭代器的__next__方法!

这样写的话可以实现函数的解耦,不会像上面代码一样,还要实例化迭代器gen = SkipElementsGenerator(my_list),然后人为迭代迭代器。

这样写的话,只需要像原来一个,实例化自定义的数据类型,直接用for或者iter来获得迭代器,代码中不会出现迭代器的名字,无感使用。

相关推荐
捕鲸叉9 分钟前
C++设计模式和编程框架两种设计元素的比较与相互关系
开发语言·c++·设计模式
未知陨落1 小时前
数据结构——二叉搜索树
开发语言·数据结构·c++·二叉搜索树
大波V51 小时前
设计模式-参考的雷丰阳老师直播课
java·开发语言·设计模式
无敌最俊朗@1 小时前
unity3d————接口基础知识点
开发语言·c#
winfredzhang1 小时前
如何使用 python 中的 Pillow 创建可自定义的图标生成器
python·pillow·图标·png
一丝晨光2 小时前
gcc 1.c和g++ 1.c编译阶段有什么区别?如何知道g++编译默认会定义_GNU_SOURCE?
c语言·开发语言·c++·gnu·clang·gcc·g++
qq_273900232 小时前
pytorch detach方法介绍
人工智能·pytorch·python
南城花随雪。2 小时前
Spring框架之装饰者模式 (Decorator Pattern)
java·开发语言·装饰器模式
究极无敌暴龙战神X2 小时前
前端学习之ES6+
开发语言·javascript·ecmascript
虞书欣的62 小时前
Python小游戏24——小恐龙躲避游戏
开发语言·python·游戏·小程序·pygame