目录
为了让函数在执行退出后保留状态,可以通过闭包、全局变量等方法,还有一种简单又安全的方法,即生成器(generator);
生成器用于在 Python 中实现协同程序的效果【协同程序:可以运行的独立函数调用,函数可以暂停或挂起,并在需要时从函数离开的放继续或重新开始】;
- 生成器 是一个用于创建迭代器的简单而强大的工具。 它们的写法类似于标准的函数,但当它们要返回数据时会使用 yield 语句。 每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。
- 另一个关键特性在于局部变量和执行状态会在每次调用之间自动保存。 这使得该函数相比使用 self.index 和 self.data 这种实例变量的方式更易编写且更为清晰。
- 除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration。 这些特性结合在一起,使得创建迭代器能与编写常规函数一样容易。
可以通过 yield 语句替换函数中的 return 语句,或者使用生成器表达式,两种方法编写生成器;
使用 yield 语句编写生成器,暂停位置即为每个 yield 语句执行后;使用生成器表达式,暂停位置在表达式每个循环执行后;
生成器暂时挂起函数,并保留函数的局部变量等数据,之后可以如迭代器一样,通过 next() 方法或 for 循环再次调用生成器,从上次暂停的位置继续执行,获取生成器的下一个值;
如果生成器函数调用了return,或者执行到函数的末尾,会出现一个StopIteration异常;
- #使用 yiled 语句编写生成器函数
- def test(var1, var2, var3):
- yield var1
- yield var2
- yield var3
-
- #创建生成器 x
- x = test(1,2,3)
- x
- <generator object test at 0x0000025584721120>
-
- #通过 next() 方法调用生成器,依次获取生成器的值,直到抛出 StopIteration 错误
- next(x)
- 1
- next(x)
- 2
- next(x)
- 3
- next(x)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- StopIteration
-
- #使用生成器表达式创建生成器 y
- y = (i for i in (1,2,3))
- y
- <generator object <genexpr> at 0x00000255847215F0>
-
- #通过 for 循环调用生成器,依次获取生成器的值
- for i in y:
- print(i)
-
- 1
- 2
- 3
-
- #再次调用,抛出 StopIteration 错误
- next(y)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- StopIteration
生成器因为是通过暂停、重新调用的方式不断获取数值,对于一些无限循环或者极大循环而言,可以通过生成器依次获取需要的数值,而不用担心一次性生成全部数值数据量过大无法存储。
- #创建一个可以无限循环的斐波那契数列
- def test():
- f1 = 0
- f2 = 1
- while True:
- f1, f2 = f2, f1 + f2
- yield f1
-
- #通过生成器可以依次获取需要的数值,不用担心一次性生成数值过大
- x = test()
- for i in x:
- if i < 50:
- print(i)
- else:
- break
-
- 1
- 1
- 2
- 3
- 5
- 8
- 13
- 21
- 34
总的来说,generator 像是用来产生一系列值的特殊类型迭代器,yield 语句则像是生成器用于保存函数中途返回结果的魔法方法,和迭代器相似,我们可以通过使用next()来从generator中获取下一个值 。
yield表达式,两种原型语法结构如下:
- yield expression # 全部Python
-
- yield from iterable # 3.3和以后版本
yield表达式定义一个生成器函数,按需产生结果,包含yield的函数被专门编译;
在调用时,他们创建和返回一个生成器对象——一个自动支持迭代协议在迭代环境中提供结果的迭代;
通常在一个迭代中,yield 将函数状态挂起,并返回一个 expression(表达式)值;在下一个迭代中,函数先前的位置和变量状态被恢复,并在 yield 语句之后立即恢复控制;
Python 3.3之后支持yield from iterable ,下面两种格式等价:
- for i in range(N): yield i # 所有Python
-
- yield from range(N) # 3.3和以后的选项
生成器支持 next() 函数,逐个读取生成器直到没有可读结果报错,但不能使用下标。
- #通过yield表达式,逐个生成斐波那契数
- def funx(var1,var2):
- while True:
- var1,var2 = var2,var1 + var2
- yield var1
-
- t = funx(0,1)
-
- next(t)
- 1
- next(t)
- 1
- next(t)
- 2
- next(t)
- 3
- next(t)
- 5
-
- #通过 next() 函数,逐个读取生成器直到没有可读结果报错,
- def funx():
- for i in range(5):
- yield i
-
- t = funx()
- next(t)
- 0
- next(t)
- 1
- next(t)
- 2
- next(t)
- 3
- next(t)
- 4
- next(t)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- StopIteration
-
- #使用 from 语句,等价上方函数
- def funx():
- yield from range(5)
-
- t = funx()
- next(t)
- 0
- next(t)
- 1
- next(t)
- 2
- next(t)
- 3
- next(t)
- 4
- next(t)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- StopIteration
生成器逐个生成,但也可以通过 list() 、tuple() 等函数将结果全部生成并返回,此时再执行next(),或者 list() 、tuple() ,返回为空。
- def funx():
- yield from range(5)
-
-
- #将生成器结果一次性生成并转换为列表,再次执行返回为空
- t = funx()
- list(t)
- [0, 1, 2, 3, 4]
- list(t)
- ()
-
- #将生成器结果一次性生成并转换为元组,再次执行返回为空
- t = funx()
- tuple(t)
- (0, 1, 2, 3, 4)
- tuple(t)
- ()
-
- #计算生成器所有结果之和,再次执行返回0
- t = funx()
- sum(t)
- 10
- sum(t)
- 0
某些简单的生成器可以写成简洁的表达式代码,所用语法类似列表推导式,但外层为圆括号而非方括号,语法结构如下:
generator_expression = ( expression comp_for )
- 在生成器表达式中使用的变量会在为生成器对象调用 __next__() 方法的时候以惰性方式被求值(即与普通生成器相同的方式)。
- 最左侧 for 子句内的可迭代对象是会被立即求值的,因此它所造成的错误会在生成器表达式被定义时被检测到,而不是在获取第一个值时才出错。
- 后续的 for 子句以及最左侧 for 子句内的任何筛选条件无法在外层作用域内被求值,因为它们可能会依赖于从最左侧可迭代对象获取的值。 例如: (x*y for x in range(10) for y in range(x, x+10)).
这种表达式被设计用于生成器将立即被外层函数所使用的情况,生成器表达式相比完整的生成器更紧凑但较不灵活,相比等效的列表推导式则更为节省内存;
不同于列表推导式,生成器表达式逐个生成,而不像列表推导式一次性全部产生。
- #生成器表达式
- t = (i for i in range(5))
- type(t)
- <class 'generator'>
- t
- <generator object <genexpr> at 0x000001570A1FFAC0>
-
- next(t)
- 0
- next(t)
- 1
- next(t)
- 2
- next(t)
- 3
- next(t)
- 4
- next(t)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- StopIteration