Python中的yield关键字及表达式、生成器、生成器迭代器、生成器表达式详解

  在Python中for循环运行机制探究以及可迭代对象、迭代器详解中,我们结合对for循环机制的探究,深入分析了Python中可迭代对象、迭代器等概念。实际上,除此之外,Python中还有另一个很重要的概念生成器。

  1. yield关键字及表达式、生成器、生成器迭代器、生成器表达式

  1.1 yield关键字及表达式(yield expression)

  1.1.1 yield关键字

  在了解yield表达式之前,先了解一下这个关键字。Python官方文档中对其定义为:

  Each yield temporarily suspends prcessing, remembering the location execution state (including local variables and pending try-statements).

  每一个yield会暂时挂起当前处理,记录当前位置的执行状态(包括局部变量等)。

  When the generator iterator resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation).

  当生成器迭代器重新执行,yield将会从其上次挂起之处继续执行,而不是像普通函数一样每调用一次都重新开始执行。

  下面代码演示了yield关键字的作用:

  def create_fibonacci(num_of_sequence):

  current_num, next_num = 0, 1

  current_index = 0

  while current_index < num_of_sequence:

  # 程序执行到此处时,会:

  # 1.返回current_num的值;

  # 2.保存当前的状态,包括所有局部变量的状态

  # 3.下次执行时,从yield current_num的下一句开始执行

  yield current_num

  current_num, next_num = next_num, current_num + next_num

  current_index += 1

  def main():

  fib_generator_iterator = create_fibonacci(8)

  # 通过next()函数一次获取一个生成器迭代器中的值

  print(next(fib_generator_iterator))

  print(next(fib_generator_iterator))

  print(next(fib_generator_iterator))

  print(next(fib_generator_iterator))

  print(next(fib_generator_iterator))

  print(next(fib_generator_iterator))

  if __name__ == '__main__':

  main()

  1.1.2 yield表达式

  对于yield表达式,实际上,因为yield作为关键字并不能单独使用,所以该关键字总是以表达式的形式出现在代码中,如上述代码第9行的yield current_num就是一个yield表达式。

  具体地,在Python官方文档中,关于yield表达式,有:

  The yield expression is used when defining a generator function or an asynchronous generator function and thus can only be used in the body of a function definition.

  yield表达式在定义生成器函数或异步生成器函数时使用,因此,yield表达式只能在定义的函数体中使用。

  1.2 生成器(generator)

  Python官方文档中,对于生成器的定义为:

  A function which returns a generator iterator.

  生成器是一个可以返回生成器迭代器的函数。

  It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

  生成器看来像一个普通函数,但生成器和普通函数的区别在于,生成器中包含yield表达式,该yield表达式可以生成一系列可用于for循环的值,这些值还可以通过next()函数一次获取一个。

  1.3 生成器迭代器(generator iterator)

  Python官方文档中,对于生成器迭代器的定义为:

  An object created by a generator function.

  生成器迭代器是一个由生成器函数创建的对象

  实际上,生成器迭代器是一种特殊的迭代器,这一点从其名字中即可以印证:即用生成器来修饰迭代器得到生成器迭代器的名字。故所有接受迭代器的地方也都支持生成器迭代器,如for循环,list()、tuple()等处。

  为了避免名词混淆,Python中对于生成器一词generator还做了如下解释说明:

  Usually refers to a generator function, but may refer to a generator iterator in some contexts.

  生成器一词generator一般指的是生成器函数,但是在一些上下文中也可能指生成器迭代器。

  In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.

  在字面含义不明确的地方,推荐使用全名以避免歧义。

  1.3.1 __next()__魔法方法

  This method is normally called implicitly, e.g. by a for loop, or by the built-in next() function.

  该方法通常被隐式调用,如:被for循环调用,或被內置函数next()调用。

  Starts the execution of a generator function or resumes it at the last executed yield expression.

  调用该函数将启动生成器函数开始执行或者接着从上一次执行的yield表达式处开始执行。

  When a generator function is resumed with a next() method, the current yield expression always evaluates to None.

  当一个生成器函数由__next()__方法重启后,当前的yield表达式返回的值总是None。

  The execution then continues to the next yield expression, where the generator suspended again, and the value of the expression_list is returned to next()’s caller.

  然后程序继续执行至下一个yield表达式,程序在此处又一次被暂停,且expression_list的值被返回至__next()__的调用者。

  If the generator exits without yielding another value, a StopIteration exception is raised.

  如果生成器未能返回另一个值而退出,则程序抛出StopIteration异常。

  关于上述说明,如下列代码验证:

  def create_fibonacci(num_of_sequence):

  current_num, next_num = 0, 1

  current_index = 0

  while current_index < num_of_sequence:

  ret = yield current_num

  print(">>>>>ret>>>>>", ret)

  current_num, next_num = next_num, current_num + next_num

  current_index += 1

  def main():

  fib_generator_iterator = create_fibonacci(8)

  print(next(fib_generator_iterator))

  print(next(fib_generator_iterator))

  if __name__ == '__main__':

  main()

  其运行结果为:

  0郑州妇科医院哪家好 https://m.120ask.com/zhenshi/

  >>>>>ret>>>>> None

  1

  1.3.2 send(value)方法

  Resumes the execution and “sends” a value into the generator function.

  该方法恢复程序执行并且“发送”一个值进入生成器函数中。

  The value argument becomes the result of the current yield expression.

  value形参会成为当前yield表达式的结果。

  The send() method returns the next value yielded by the generator, or raisese StopIteration if the generator exits without yielding another value.

  send()方法返回生成器产生的下一个值,或者当生成器因无法产生新值而退出时抛出StopIteration异常。

  When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.

  当调用send()方法启动生成器时,value参数必须传入None,因为可接收该值的yield表达式。

  关于上述说明,如下列代码验证:

  def create_fibonacci(num_of_sequence):

  current_num, next_num = 0, 1

  current_index = 0

  while current_index < num_of_sequence:

  ret = yield current_num

  print(">>>>>>>>ret>>>>>>>>", ret)

  current_num, next_num = next_num, current_num + next_num

  current_index += 1

  def generator_send():

  generator = create_fibonacci(10)

  print(generator.send(None))

  print(generator.send("CodingGuru"))

  def main():

  generator_send()

  if __name__ == '__main__':

  main()

  其运行结果为:

  0

  >>>>>>>>ret>>>>>>>> CodingGuru

  1

  1.4 生成器表达式(generator expression)

  Python官方文档中,对于生成器表达式的定义为:

  An expression that returns an iterator.

  生成器表达式是返回一个迭代器的表达式。

  实际上,生成器表达式和列表生成式的格式类似,如下所示:

  In [1]: [i * i for i in range(10)]

  Out[1]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  In [2]: (i * i for i in range(10))

  Out[2]: at 0x7f9916b04fc0>

  即,生成器表达式和列表推导式看起来的区别仅在于前者将后者的[expression]换成了(expression)。但实际上,二者更大的区别类似于在Python中for循环运行机制探究以及可迭代对象、迭代器详解中提及的两种数据生成策略上,也就是说:

  列表推导式一次性产生所有需要的数据,并用一个列表存储,内存的开销较大;

  生成器推导式只是给出了数据生成的方式,仅在需要时逐个生成,使用结束后会很快被回收,内存开销小得多。

  例如,如果想要求1到10平方的和,使用生成器表达式和列表推导式都可以,如:

  In [3]: sum(i * i for i in range(10))

  Out[3]: 285

  In [4]: sum([i * i for i in range(10)])

  Out[4]: 285

  但是当增加range的上界(如在我的8GB内存8核CPU的设备上增加至100,000,000),列表推导式方法将很快因内存占用过大导致计算机无法运行出结果,而生成器表达式却可以承受更大的上界。

  Using a yield expression in a function’s body causes that function to be a generator, and using it in an async def function’s body causes that coroutine function to be an asynchronous generator.

  在函数体中使用yield表达式将使得该函数成为一个生成器,而在用关键字async定义的异步函数体重使用yield表达式将使得该函数成为一个异步生成器。


猜你喜欢

转载自blog.51cto.com/14503791/2497381