Python中的迭代器与生成器使用及说明

目录
  • 一、迭代器(Iterator)
    • 1.1 可迭代对象(Iterable)
    • 1.2 将可迭代对象转化为迭代器
    • 1.3 构造迭代器
  • 二、生成器(Generator)
    • 2.2 使用带有 yield 关键字的函数构造生成器
  • 总结

一、迭代器(Iterator)

1.1 可迭代对象(Iterable)

可迭代对象,可以简单理解为可遍历对象,即能够使用 for 循环遍历的对象。Python中常见的可迭代对象有:列表、元组、字符串、集合、range、字典等。

迭代器和生成器都是可迭代对象。

对于Python中的任意对象,只要它定义了可以返回一个迭代器的 __iter__ 方法,或者定义了可以支持下标索引的 __getitem__ 方法,那么它就是一个可迭代对象。

对可迭代对象使用 __iter__ 方法后,会返回一个迭代器。

如何判断一个对象是否为可迭代对象呢?请看下例。

from collections.abc import Iterable

isinstance([1, 2, 3], Iterable)  # True
isinstance((1, 2, 3), Iterable)  # True
isinstance('123', Iterable)  # True
isinstance({1, 2, 3}, Iterable)  # True
isinstance(range(3), Iterable)  # True
isinstance({'key': 'value'}, Iterable)  # True
isinstance(123, Iterable)  # False

可以看出,我们只需要使用 isinstance(object, Iterable) 即可判断给定的 object 是否为可迭代对象。

严格来讲,isinstance() 只会将有 __iter__ 方法的对象判断为 Iterable。

换言之,仅用 __getitem__ 方法实现的可迭代对象会被 isinstance() 误判为不可迭代对象。

最正确的做法是直接尝试 iter(object),如果没有报错,则说明 object 是可迭代对象。

1.2 将可迭代对象转化为迭代器

我们可以将现有的可迭代对象转化为可迭代器:

s = '12345'
myiter = iter(s)
myiter
# <str_iterator at 0x25e6f40d130>

不断调用 next 方法来依次获取迭代器的元素:

next(myiter)
# '1'
next(myiter)
# '2'
next(myiter)
# '3'
next(myiter)
# '4'
next(myiter)
# '5'
next(myiter)
# StopIteration: 

可见迭代器执行到最后时会抛出一个 StopIteration 异常。

为避免这种异常,我们完全可以用更简单的 for 循环去遍历:

for e in myiter:
    print(e)
# 1
# 2
# 3
# 4
# 5

1.3 构造迭代器

构造一个迭代器只需要在自定义的类中实现两个方法:__iter__ 和 __next__ 。

  • 迭代器是一个可以记住遍历位置的对象。
  • 迭代器对象会从第一个元素开始访问,直到所有元素都被访问为止,且只能前进不能后退。

当我们构造类时,必须要有一个名为 __init__() 的函数,该函数可以在实例化时进行一些初始化。

  • __iter__() 方法的行为类似,可以执行操作(初始化等),但必须始终返回迭代器对象本身。
  • __next__() 方法还允许你进行其他操作,并且必须返回序列中的下一项。
class MyIter:
    def __iter__(self):
        self.count = 1
        return self
    
    def __next__(self):
        x = self.count
        self.count += 1
        return x

我们创建了一个返回数字的迭代器,每次序列的数值都将 +1。

myiter = iter(MyIter())
next(myiter)
# 1
next(myiter)
# 2
next(myiter)
# 3

如果我们一直调用 next() 的方法,则序列的值将会无限递增下去。即如果我们使用 for 循环去遍历上述迭代器,循环将永远进行下去…

myiter = iter(MyIter())
for e in myiter:
    print(e)
# 循环将一直进行下去...

为了防止迭代永远进行下去,我们可以在迭代次数达到一定值时抛出 StopIteration 异常。

class MyIter:
    def __iter__(self):
        self.count = 1
        return self
    
    def __next__(self):
        if self.count <= 5:
            x = self.count
            self.count += 1
            return x
        else:
            raise StopIteration

这样再执行 for 循环就不会一直进行下去了:

myiter = iter(MyIter())
for e in myiter:
    print(e)
# 1
# 2
# 3
# 4
# 5

二、生成器(Generator)

在Python中,一边迭代(循环)一边计算的机制,称为生成器。生成器能够迭代的关键是因为它有一个 __next__ 方法。

为什么要有生成器呢?我们知道,列表中的所有数据都存储在内存中,如果有海量数据的话将会非常消耗内存。很多时候,我们只需要访问列表中前面的元素,这样一来后面的元素所占用的空间就白白浪费了。

如果列表元素能够按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的列表,从而节省了大量的空间(即用多少就生成多少)。

有以下两种常用方法来创建生成器:

将列表解析式中的 [] 改为 ()。在自定义的函数中使用 yield 关键字。此时这个函数就不再是一个普通函数,而是一个生成器,调用该函数就是创建了一个生成器对象。

2.1 使用 () 构造生成器

比较以下两段代码:

a = [x for x in range(3)]
type(a)
# list
a = (x for x in range(3))
type(a)
# generator

我们还可以比较列表解析式和生成器的耗时:

tic = time.time()
a = sum([x for x in range(10000000)])
toc = time.time()
print(toc - tic)
# 0.9081981182098389
tic = time.time()
a = sum((x for x in range(10000000)))
toc = time.time()
print(toc - tic)
# 0.6906485557556152

我们当然可以对生成器使用 next() 方法:

next(a)
# 0
next(a)
# 1
next(a)
# 2
next(a)
# StopIteration: 

但一般我们不会用 next() 来获取下一个返回值,而是直接使用 for 循环来迭代。

2.2 使用带有 yield 关键字的函数构造生成器

带有 yield 的函数不再是一个普通函数,而是一个生成器。

yield 相当于return一个值,并且记住这个返回的位置,下次迭代时,代码从 yield 的下一条语句开始执行。

我们可以通过下面的例子先来理解一下:

def num():
    print('开始执行')
    for i in range(5):
        yield i
        print('继续执行')

mygen = num()
type(mygen)
# generator

由此,我们成功创建了一个生成器对象。接下来调用 next 方法观察这个生成器是如何工作的:

next(mygen)
# 开始执行
# 0
next(mygen)
# 继续执行
# 1
next(mygen)
# 继续执行
# 2
next(mygen)
# 继续执行
# 3
next(mygen)
# 继续执行
# 4
next(mygen)
# StopIteration:

当然我们也可以使用 for 循环来遍历这个生成器:

for step in mygen:
    print(step)
# 开始执行
# 0
# 继续执行
# 1
# 继续执行
# 2
# 继续执行
# 3
# 继续执行
# 4
# 继续执行

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • python memory_profiler库生成器和迭代器内存占用的时间分析

    不进行计算时,生成器和list空间占用 import time from memory_profiler import profile @profile(precision=4) def list_fun(): start = time.time() total = ([i for i in range(5000000)]) print('iter_spend_time:',time.time()-start) @profile(precision=4) def gent_func(): gen

  • python中的生成器、迭代器、装饰器详解

    一.装饰器 由于一个函数能实现一种功能,现在想要在不改变其代码的情况下,让这个函数进化一下,即能保持原来的功能,还能有新的"技能",怎么办? 现已经存在一个自定义的函数func1 def func1(): print('hello,world!') 让func1进化一下:(继承func1之前的所有功能,而且还有新的‘技能’) 效果和下面定义的函数func2效果是一样的 def func2(): func1() #调用func1,即可保持func1这一函数的所有的功能都被这个新的函数继承

  • python列表生成器常用迭代器示例详解

    目录 列表生成式基础语法 1. 使用列表生成式,一行解决for循环 2. 双层循环 3. 加判断语句,条件过滤 4. 加入函数 5. 常见几种迭代器:range. zip . enumerate . filter . reduce 列表生成式基础语法 [exp for iter_var in iterable (if conditional)] 原理: 首先迭代 iterable 里所有内容,每一次迭代,都把iterable里相应的内容放在iter_var中,再把表达式exp应用该iter_va

  • Python中for循环可迭代对象迭代器及生成器源码学习

    目录 问题: 1. 迭代 1.1 可迭代对象Iterable 1.2 迭代器Iterator 1.3 for循环 1.3.1 iter()方法和next()方法 1.3.2 iter()和__iter__() 1.3.3 next()和__next__() 1.3.4 自定义类实现__iter__()和__next__() 1.3.5 探究for循环 2 生成器 2.1 获取生成器 2.2 next(生成器) 2.3 生成器和迭代器 2.4 生成器解析式 问题: 之前在学习list和dict相关

  • 一文搞懂python 中的迭代器和生成器

    可迭代对象和迭代器 迭代(iterate)意味着重复,就像 for 循环迭代序列和字典那样,但实际上也可使用 for 循环迭代其他对象:实现了方法 __iter__ 的对象(迭代器协议的基础).__iter__方法返回一个迭代器,它是包含方法 __next__ 的对象,调用时可不提供任何参数:当你调用 __next__ 时,迭代器应返回其下一个值:如果没有可供返回的值,应引发 StopIteration 异常:也可使用内置函数 next(),此种情况下,next(it) 与 it.__next(

  • 举例讲解Python中的迭代器、生成器与列表解析用法

    迭代器:初探 上一章曾经提到过,其实for循环是可用于任何可迭代的对象上的.实际上,对Python中所有会从左至右扫描对象的迭代工具而言都是如此,这些迭代工具包括了for循环.列表解析.in成员关系测试以及map内置函数等. "可迭代对象"的概念在Python中是相当新颖的,基本这就是序列观念的通用化:如果对象时实际保存的序列,或者可以再迭代工具环境中一次产生一个结果的对象,那就看做是可迭代的. >>文件迭代器 作为内置数据类型的文件也是可迭代的,它有一个名为__next_

  • Python中的迭代器与生成器高级用法解析

    迭代器 迭代器是依附于迭代协议的对象--基本意味它有一个next方法(method),当调用时,返回序列中的下一个项目.当无项目可返回时,引发(raise)StopIteration异常. 迭代对象允许一次循环.它保留单次迭代的状态(位置),或从另一个角度讲,每次循环序列都需要一个迭代对象.这意味我们可以同时迭代同一个序列不只一次.将迭代逻辑和序列分离使我们有更多的迭代方式. 调用一个容器(container)的__iter__方法创建迭代对象是掌握迭代器最直接的方式.iter函数为我们节约一些

  • 深入讲解Python中的迭代器和生成器

    在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的,就要了解一下迭代器相关的知识了. 迭代器 迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身:next()方法返回容器的下一个元素,在结尾时引发StopIteration异常. __iter__()和next()

  • Python中的迭代器与生成器使用及说明

    目录 一.迭代器(Iterator) 1.1 可迭代对象(Iterable) 1.2 将可迭代对象转化为迭代器 1.3 构造迭代器 二.生成器(Generator) 2.2 使用带有 yield 关键字的函数构造生成器 总结 一.迭代器(Iterator) 1.1 可迭代对象(Iterable) 可迭代对象,可以简单理解为可遍历对象,即能够使用 for 循环遍历的对象.Python中常见的可迭代对象有:列表.元组.字符串.集合.range.字典等. 迭代器和生成器都是可迭代对象. 对于Pytho

  • pytorch::Dataloader中的迭代器和生成器应用详解

    在使用pytorch训练模型,经常需要加载大量图片数据,因此pytorch提供了好用的数据加载工具Dataloader. 为了实现小批量循环读取大型数据集,在Dataloader类具体实现中,使用了迭代器和生成器. 这一应用场景正是python中迭代器模式的意义所在,因此本文对Dataloader中代码进行解读,可以更好的理解python中迭代器和生成器的概念. 本文的内容主要有: 解释python中的迭代器和生成器概念 解读pytorch中Dataloader代码,如何使用迭代器和生成器实现数

  • python 协程中的迭代器,生成器原理及应用实例详解

    本文实例讲述了python 协程中的迭代器,生成器原理及应用.分享给大家供大家参考,具体如下: 1.迭代器理解 迭代器: 迭代器是访问可迭代对象的工具 迭代器是指用iter(obj)函数返回的对象(实例) 迭代器是指用next(it)函数获取可迭代对象的数据 迭代器函数(iter和next) iter(iterable)从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象 next(iterator) 从迭代器iterator中获取下一了记录,如果无法获取下一条记录,则触发

  • python中的迭代器,生成器与装饰器详解

    目录 迭代器 生成器 装饰器 总结 迭代器 每一个可迭代类内部都要实现__iter__()方法,返回一个迭代类对象,迭代类对象则定义了这个可迭代类如何迭代. for循环调用list本质上是是调用了list的迭代器进行迭代. # 对list进行for循环本质上是调用了list的迭代器 list = [1,2,3,4] # for 循环调用 for elem in list: print(elem) # 迭代器调用 list_iter = list.__iter__() while True: tr

  • JavaScript中的迭代器和生成器详解

    处理集合里的每一项是一个非常普通的操作,JavaScript提供了许多方法来迭代一个集合,从简单的for和for each循环到 map(),filter() 和 array comprehensions(数组推导式).在JavaScript 1.7中,迭代器和生成器在JavaScript核心语法中带来了新的迭代机制,而且还提供了定制 for-in 和 for each 循环行为的机制. 迭代器 迭代器是一个每次访问集合序列中一个元素的对象,并跟踪该序列中迭代的当前位置.在JavaScript中

  • 深入浅析Python中的迭代器

    目录结构: contents structure [-] 在开始文章之前,先贴上一张Iterable.Iterator与Generator之间的关系图: 1. Iterator VS Iterable 迭代器(Iterator) 迭代器是实现了迭代器协议的类对象,迭代器协议规定了迭代器类必需定义__next()__方法.当对迭代器对象调用next()方法时,对象会去调用__next()__计算迭代器的返回值. 可迭代对象(Iterable) 可迭代对象可以是任何对象,不一定是能返回迭代器的数据结

随机推荐