T O P

[资源分享]     迭代器生成器阅读【Python3.8官网文档】

  • By - 楼主

  • 2021-09-10 00:00:28
  • 1、迭代器

    for element in [1, 2, 3]:
        print(element)
    for element in (1, 2, 3):
        print(element)
    for key in {'one':1, 'two':2}:
        print(key)
    for char in "123":
        print(char)
    for line in open("myfile.txt"):
        print(line, end='')
    

    在幕后,for 语句会在容器对象(即列表、元组等)上调用 iter()

    iter() 返回一个定义了 __next__() 方法的迭代器对象,__next__()将逐一访问容器中的元素。 当元素用尽时,__next__() 将引发 StopIteration 异常来通知终止 for 循环。

    可以使用 next() 内置函数来调用 __next__() 方法。这个例子显示了它的运作方式:

    >>> s = 'abc'
    >>> it = iter(s)
    >>> it
    <iterator object at 0x00A1DB50>
    >>> next(it)
    'a'
    >>> next(it)
    'b'
    >>> next(it)
    'c'
    >>> next(it)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
        next(it)
    StopIteration
    

    给类添加迭代器行为:定义一个 __iter__() 方法来返回一个带有 __next__() 方法的对象。 如果类已定义了 __next__(),则 __iter__() 可以简单地返回 self:

    class Reverse:
        """Iterator for looping over a sequence backwards."""
        def __init__(self, data):
            self.data = data
            self.index = len(data)
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.index == 0:
                raise StopIteration
            self.index = self.index - 1
            return self.data[self.index]
    
    >>> rev = Reverse('spam')
    >>> iter(rev)
    <__main__.Reverse object at 0x00A1DB50>
    >>> for char in rev:
    ...     print(char)
    ...
    m
    a
    p
    s
    

    2、生成器

    2.1、yield 表达式

    yield_atom       ::=  "(" yield_expression ")"
    yield_expression ::=  "yield" [expression_list | "from" expression]
    

    只能在函数定义的内部使用 yield 表达式,在一个函数体内使用 yield 表达式会使这个函数变成一个生成器函数

    def gen():  # defines a generator function
        yield 123
    

    2.2、简要理解概念

    官网原文

    generator--生成器

    返回一个 generator iterator 的函数。
    
    它看起来很像普通函数,不同点在于其包含 yield 表达式,以便产生一系列值供给 for 循环使用或是通过 next() 函数逐一获取。
    
    通常是指生成器函数,但在某些情况下也可能是指生成器迭代器。如果需要清楚表达具体含义,请使用全称以避免歧义。
    

    generator iterator--生成器迭代器

    generator 函数所创建的对象。
    
    每个 yield 会临时暂停处理,记住当前位置执行状态(包括局部变量和挂起的 try 语句)。
    
    当该生成器迭代器恢复时,它会从离开位置继续执行(这与每次调用都从新开始的普通函数差别很大)。
    

    generator expression--成器表达式

    返回一个迭代器的表达式。
    
    它看起来很像普通表达式后面带有定义了一个循环变量、范围的 for 子句,以及一个可选的 if 子句。
    

    以下复合表达式会为外层函数生成一系列值:

    >>> sum(i*i for i in range(10))         # sum of squares 0, 1, 4, ... 81
    285
    

    2.3、生成器-迭代器的方法

    被用于控制生成器函数的执行。

    请注意:生成器已经在执行时,调用以下任何方法都会引发 ValueError 异常。

    • generator.next()

      开始一个生成器函数的执行,或从上次执行的 yield 表达式位置恢复执行

      当一个生成器函数通过 __next__() 方法恢复执行时,当前的 yield 表达式总是取值为 None。【注:注意区分 yield 表达式的值和返回给调用者的值】

      随后会继续执行到下一个 yield 表达式,其 expression_list 的值会返回给 __next__() 的调用者。

      如果生成器没有产生下一个值就退出,则将引发 StopIteration 异常。

      此方法通常是隐式地调用,例如通过 for 循环或是内置的 next() 函数

    • generator.send(value)

      恢复执行,并向生成器函数“发送”一个值。 value 参数将成为当前 yield 表达式的结果

      send() 方法会返回生成器所产生的下一个值,或者如果生成器没有产生下一个值就退出则会引发 StopIteration。

      当调用 send() 来启动生成器时,它必须以 None 作为调用参数,因为这时没有可以接收值的 yield 表达式

    • generator.throw(type[, value[, traceback]])

      在生成器暂停的位置引发 type 类型的异常,并返回该生成器函数所产生的下一个值。

      如果生成器没有产生下一个值就退出,则将引发 StopIteration 异常。

      如果生成器函数没有捕获传入的异常,或引发了另一个异常,则该异常会被传播给调用者。

    • generator.close()

      在生成器函数暂停的位置引发 GeneratorExit。

      如果之后生成器函数正常退出、关闭或引发 GeneratorExit (由于未捕获该异常)则关闭并返回其调用者。 如果生成器产生了一个值,关闭会引发 RuntimeError。

      如果生成器引发任何其他异常,它会被传播给调用者。 如果生成器已经由于异常或正常退出则 close() 不会做任何事。

    2.4、生成器理解

    # 理解生成器的工作过程
    >>> def reverse(data):
    ...     for index in range(len(data)-1, -1, -1):
    ...         yield data[index]
    ...
    
    >>> for char in reverse('golf'):
    ...     print(char)
    ...
    f
    l
    o
    g
    
    # next(iterator[, default]) : Return the next item from the iterator.
    # 上面的for循环相当于:
    >>> g = reverse('golf')
    >>> next(g)
    'f'
    >>> next(g)
    'l'
    >>> next(g)
    'o'
    >>> next(g)
    'g'
    
    # 或相当于:
    >>> g.__next__()
    'f'
    >>> g.__next__()
    'l'
    >>> g.__next__()
    'o'
    >>> g.__next__()
    'g'
    >>> g.__next__()   # 迭代完了
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    当一个生成器函数被调用的时候,它返回一个迭代器,称为生成器迭代器。

    然后这个生成器迭代器来控制生成器函数的执行。当这个生成器迭代器的某一个方法被调用的时候【注:如__next__()】,生成器函数开始执行。

    这时会一直执行到第一个 yield 表达式,在此执行再次被挂起,给生成器迭代器的调用者返回 expression_list 的值。【注:也就是返回'f''i'等值。参考 yield 表达式的语法格式部分】

    挂起后,所有局部状态都被保留下来,包括局部变量的当前绑定,指令指针,内部求值栈和任何异常处理的状态。

    通过调用生成器迭代器的某一个方法,生成器函数继续执行,此时函数的运行就和 yield 表达式只是一个外部函数调用的情况 完全一致。【注:继续执行时直接调用 yield 表达式】

    重新开始后,yield 表达式的值取决于调用的哪个方法来恢复执行。 如果用的是 __next__() (通常通过语言内置的 for 或是 next() 来调用) 那么结果就是 None。否则,如果用 send(), 那么结果就是传递给 send 方法的值。
    【注:是“yield 表达式的值”;参考下一节的generator.__next__()方法理解】

    # 理解:重新开始后,yield 表达式的值取决于调用的哪个方法来恢复执行。
    >>> def reverse(data):
    ...     for index in range(len(data)-1, -1, -1):
    ...         y = yield data[index]
    ...         print(y)
    ...
    >>> g = reverse('golf')
    >>> g
    <generator object reverse at 0x000001EE2B4875E8>
    >>> g.__next__()
    'f'
    >>> g.__next__()
    None
    'l'
    ...
    
    >>> g = reverse('golf')
    >>> g.send(None)
    'f'
    >>> g.send('aa')
    aa
    'l'
    ...
    

    2.5、例子

    # 整体理解生成器
    >>> def echo(value=None):
    ...     print("Execution starts when 'next()' is called for the first time.")
    ...     try:
    ...         while True:
    ...             try:
    ...                 value = (yield value)   
    ...             except Exception as e:
    ...                 value = e
    ...     finally:
    ...         print("Don't forget to clean up when 'close()' is called.")
    ...
    >>> generator = echo(1)
    >>> print(next(generator))
    Execution starts when 'next()' is called for the first time.
    1
    >>> print(next(generator))  # 迭代器里没有值了
    None
    
    # 恢复执行,并向生成器函数“发送”一个值。 value 参数将成为当前 yield 表达式的结果
    >>> print(generator.send(2))  # 这里的2发送给了`value = (yield value)`里的左边的value
    2
    >>> generator.throw(TypeError, "spam")
    TypeError('spam',)
    >>> generator.close()
    Don't forget to clean up when 'close()' is called.
    

    3、生成器表达式

    生成器表达式是用圆括号括起来的紧凑形式生成器标注。

    generator_expression ::=  "(" expression comp_for ")"
    

    生成器表达式会产生一个新的生成器对象。其句法与推导式相同,区别在于它是用圆括号而不是用方括号或花括号括起来的

    在生成器表达式中使用的变量会在为生成器对象调用 __next__() 方法的时候以惰性方式被求值(即与普通生成器相同的方式)。

    但是,最左侧 for 子句内的可迭代对象是会被立即求值的,因此它所造成的错误会在生成器表达式被定义时被检测到,而不是在获取第一个值时才出错。

    后续的 for 子句以及最左侧 for 子句内的任何筛选条件无法在外层作用域内被求值,因为它们可能会依赖于从最左侧可迭代对象获取的值。

    例如:

    (x*y for x in range(10) for y in range(x, x+10)).
    

    圆括号在只附带一个参数的调用中可以被省略。 详情参见 调用 一节。

    为了避免干扰到生成器表达式本身的预期操作,禁止在隐式定义的生成器中使用 yield 和 yield from 表达式


    英文官方文档

    中文官方文档

    帮助理解

    本帖子中包含资源

    您需要 登录 才可以下载,没有帐号?立即注册