开发者

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

开发者 https://www.devze.com 2022-12-17 09:31 出处:网络 作者: Lareges
目录一、迭代器(Iterator)1.1 可迭代对象(Iterable)1.2 将可迭代对象转化为迭代器1.3 构造迭代器二、生成器(Generator)2.2 使用带有 yield 关键字的函数构造生成器总结一、迭代器(Iterator)
目录
  • 一、迭代器(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
    isijsnstance({'key': 'vawww.devze.comlue'}, Iterable) # True
    isinstance(123, Iterable)javascript # 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)
    # StopIteratihttp://www.devze.comon:

    可见迭代器执行到最后时会抛出一个 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:
      prinhttp://www.devze.comt(step)
    # 开始执行
    # 0
    # 继续执行
    # 1
    # 继续执行
    # 2
    # 继续执行
    # 3
    # 继续执行
    # 4
    # 继续执行

    总结

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

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号