一句话解释:包含了yield关键字的函数就是生成器,它的返回值是一个生成器对象。
Python 中的生成器(Generator)是十分有用的工具,它能够方便地生成迭代器(Iterator)。
- def my_gen():
- yield 1
- yield 2
- yield 3
-
-
- gen = my_gen()
-
- #生成器可以由next()调用
- while True:
- try:
- next(gen)
- except StopIteration:
- print('Done..')
- break
-
- #next() 调用太啰嗦,通常我们用迭代的方式获取生成器的值:
- gen = my_gen()
- for item in gen:
- print(item)
生成器还有一种更简单的写法,像这样:
- # 列表推导式
- my_list = [x for x in range(10000)]
-
- # 生成器表达式
- my_gen = (x for x in range(10000))
1.yield相当于return。
2.函数遇到yield就暂停,保存当前信息,返回yield的值。
3.在下次执行next()时,从当前位置继续执行。

yield关键字有两点作用:
保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
将 yield 关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
验证
- def Fibonacci(all_nums):
- a, b = 0, 1
- current_num = 0
- print("----1----")
- while current_num < all_nums:
- print("----2----")
- yield a # 如果一个函数中有yield语句,那么这个就不再是函数了,而是一个生成器的模板
- print("----3----")
- a, b = b, a+b
- print("----4----")
- current_num += 1
- print("----5----")
- return "done"
-
- # 如果在调用Fibonacci的时候,发现这个函数中有yield,那么此时不再是调用函数,而是创建了一个生成器对象
- fib = Fibonacci(5)
-
- ret = next(fib)
- print("----6----")
- print(ret)
-
- ret = next(fib)
- print("----7----")
- print(ret)
-
- ret = next(fib)
- print("----9----")
- print(ret)
假如你读取大文件这样写,通常这都是没啥问题的。但如果这个文件非常非常大,那么将会得到内存溢出的报错。
- def csv_reader(file_name):
- file = open(file_name)
- result = file.read().split("\n")
- return result
可以用生成器这样写
由于这个版本的 csv_reader() 是个生成器,因此你可以通过遍历,加载一行、处理一行,从而避免了内存溢出的问题。
- def csv_reader(file_name):
- for row in open(file_name, "r"):
- yield row
由于生成器一次只生成一个值,因此它可用于表示无限数据。
- def all_even():
- n = 0
- while True:
- yield n
- n += 2
-
- even = all_even()
- for i in even:
- print(i)
- def gen():
- for x in range(10000):
- yield x
-
- # 生成器
- my_gen = gen()
- # 列表
- my_list = [x for x in range(10000)]
比较他们的大小
- >>> import sys
-
- >>> sys.getsizeof(my_list)
- 87616
- >>> sys.getsizeof(my_gen)
- 112
有时候你需要把两个生成器组合成一个新的生成器,比如:
- gen_1 = (i for i in range(0,3))
- gen_2 = (i for i in range(6,9))
-
- def new_gen():
- for x in gen_1:
- yield x
- for y in gen_2:
- yield y
-
- for x in new_gen():
- print(x)
-
- # 输出:
- # 0
- # 1
- # 2
- # 6
- # 7
- # 8
这种组合迭代的形式不太方便,因此 Python 3.3 引入新语法 yield from 后,可以改成这样:
- def new_gen():
- yield from gen_1
- yield from gen_2
我们除了可以使用 next() 函数来唤醒生成器继续执行外,还可以使用 send() 函数来唤醒执行。使用 send() 函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。
- def gen():
- count = 0
- while True:
- count += (yield count)
- >>> g = gen()
- >>> g.send(None)
- 0
- >>> g.send(1)
- 1
- >>> g.send(2)
- 3
- >>> g.send(5)
- 8
.throw() 允许用生成器抛出异常
- def my_gen():
- count = 0
- while True:
- yield count
- count += 1
-
- gen = my_gen()
- for i in gen:
- print(i)
- if i == 3:
- gen.throw(ValueError('The number is 3...'))
-
- # 输出:
- # 0
- # 1
- # 2
- # 3
- # ValueError: The number is 3...
.close() 可以停止生成器
- def my_gen():
- count = 0
- while True:
- yield count
- count += 1
-
- gen = my_gen()
- for i in gen:
- print(i)
- if i == 3:
- gen.close()
以上就是生成器的大致介绍了。它可以暂停控制流,并在你需要的时候随时回到控制流,从上一次暂停的位置继续执行。
生成器有助于你处理大型数据流或者表达无限序列,是生成迭代器的有用工具。