大家好,小编来为大家解答以下问题,python生成的数据在哪里,python生成器有几种写法,现在让我们一起来看看吧!
python中的强大工具——生成器
一、迭代器回顾
1、什么是迭代器
在上一篇文章中,我们讲到了迭代器,所谓迭代器就是访问集合元素的一种方式,迭代器是一个可以记住遍历位置的一个对象,它可以将集合集合中的元素挨个访问,直到访问完所有元素。
2、迭代器的原理
在上一篇文章中,我们自定义了一个对象,让该对象那个成为了可迭代对象和迭代器,并且可以通过for循环迭代出来元素python实现猴子跳台阶。那么迭代器他最重要的就是next方法,所有利用迭代器,可以在每次迭代获取数据(通过next获取)时,按照特定的规律进行生成(比如说:我们最后做的使用迭代器算斐波那契数列),但是我们在实现迭代器的时候,关于当前迭代的状态(或者说位置)需要我们自己手动去记录(定义了一个变量),进而才能根据当前的状态去生成下一个数据。那么也就是说我们需要通过记录当前的位置,并且配合next方法才能生成下一个数据,但是这种方法有一个缺点:需要我们提前将数据创建好(比如说算斐波那契数列时定义的两个变量a,b)。
3、迭代器的应用
在python中不仅for……in循环遍历的是迭代器,就连一些容器类型的转换等也需要使用迭代器
比如 :list 类型 ==> tuple类型,它也是利用迭代器将一个个元素通过next方法迭代出来,可能是装到另外一个容器中
二、生成器
1、什么是生成器
在python中一边循环一边计算的机制,我们称之为生成器(generator),它是特殊的迭代器。为什么说它是特殊的迭代器,因为它也是将一个个的数据迭代出来,不过不是使用return,而是使用 yield 关键字往出返,并且这个 yield 还会自动记录位置,或者说是挂起,当然这只是生成器的一种标志,它还有其他的方式生成。
2、为什么要有生成器
比如说你要使用一些数据,那么肯定是首先想到使用列表,或者列表推导式,那么你将该列表提前定义好放在程序中,它每次执行的时候就都要去消耗大量的内存,当你好访问全部元素那还好,那如果你仅仅只访问前几个元素,那么其他的元素不是都在占用大量的资源吗?
如果列表中的元素按某种算法算出来,那我么就可以在循环的过程中不断推算出后续的元素,这样我们就必须要创建一个完整的列表来占用资源了,也就节省了大量的内存空间。
那么就是一句话,如果想要得到庞大的数据,又想让其占用的内存较少,那么就直接上生成器吧!
3、如何创建生成器
方法一:
要想创建生成器,那么就先学会使用列表推导式,并且将列表推导式的 [] 变为 () 即可
# 列表推导式算1-5的平方
my_list = [i ** 2 for i in range(1, 6)]
print(my_list)
# 生成器1-5的平方
my_generator = (i ** 2 for i in range(1, 6))
print(my_generator)
# 使用for循环 迭代 生成器,生成器自动计算后续的值,并返回
for item in my_generator:
print(item)
输出结果:
# 列表推导式输出的结果
[1, 4, 9, 16, 25]
# 生成器输出结果,发现返回了一个generator对象,即生成器对象
<generator object <genexpr> at 0x7f396ffcaa50>
1
4
9
16
25
方法二:
创建生成器只需要在一个函数中使用 yield 关键字,那么该函数就不是普通的函数了,而是一个生成器,那么也就是说 yield 是生成器的标志,并且它就是一个生成器的模板。
# 比如要使用生成器算1-10000之内的完数
# 所谓完数就是水仙花数字,个十百位数每一位的 三次方之和 == 自身,那么就是完数
def perfect_num(start,end):
while start < end:
# 分别拿到个十百千上的数字
ge = start % 10
shi = (start % 100) // 10
bai = (start % 1000) // 100
qian = start // 1000
# 因为不包含end所有必须小于end
if start < end:
# 个十百位数每一位的 三次方之和 == 自身
if start == ge ** 3 + shi ** 3 + bai ** 3 + qian ** 3:
# 返回strat,并挂起,或者说记住此时的位置,下一次再从yield开始执行
yield start
# 不管条件是否成立都要算下一位的
start += 1
if __name__ == "__main__":
# 实例化生成器对象
rust = perfect_num(1,10000)
# 输出查看是否是一个对象
print(rust)
# 循环遍历生成器对象
for item in rust:
print(item)
4、yield关键字
1、本质
将该函数标记为生成器对象,在调用含有 yield 关键字的函数时,目标生成一个生成器对象,对这个生成器对象进行迭代时,每次迭代都只执行到 yield 关键字这块,而 yield 后边的数据 是本次迭代的产物 数据,只要不进行下一次的迭代,代码流程一直监听在 yield 这块。
2、作用
(1)保存当前的运行状态,或者说记住当前运行位置,也就是将生成器挂起,等待下一次调用后继续往后执行
(2)yield 关键字后边的 表达式作为 值返回
注意:python3中的生成器可以使用return返回最终运行的返回值,而在python2中生成器里边绝对不允许出现ruturn的返回值(也就是说python2中可以使用return,但是return后面绝对不能有数据表达式),那么生成器中如何使用return呢?
3、生成器中使用return
# 创建一个生成器函数
def work(start,end):
while start < end:
print("----------1------------")
yield start
print("----------2------------")
start += 1
print("----------3------------")
else:
return "超出生成器的迭代范围"
if __name__ == "__main":
# 使用异常捕获,捕获异常
try:
# 实例化生成器对象
w = work(1,6)
# 使用for循环遍历
for _ in range(60):
# 输出下一个生成器对象,并使用next方法进行不断唤醒
print(next(w))python
# 当超出迭代的次数
except StopIteration as a:
# 打印异常的值,即return返回的值
print(a.value)
运行结果:
----------1------------
1
----------2------------
----------3------------
----------1------------
2
----------2------------
----------3------------
----------1------------
3
----------2------------
----------3------------
----------1------------
4
----------2------------
----------3------------
----------1------------
5
----------2------------
----------3------------
超出生成器的迭代范围
总结:
那么一般情况下我们在生成器中使用ruturn的作用就是通过捕获异常,告诉客户迭代次数已经超可迭代的范围
5、唤醒生成器
(1)next( ) 函数
next () 函数用户一次性唤醒迭代器,或者说迭代出一个元素,也就是说只进行迭代一次,而生成器它虽说是特殊的迭代器,但它以然是迭代器
next(iterable)
(2)send( )函数
生成器对象.send(value)
特征:
1、send一般不会放在第一次启动生成器,如果非要这么做,那么必须传递一个参数None
2、send 里面的参数 会作为数据传递给 yield ,当作 yield 的结果,然后通过一个遍历可以接收这个结果
def work(start,end):
while start < end:
print("----------1------------")
# 使用一个遍历来接收yield的结果,因为send传递了一个参数给yield
a = yield start
print(a)
print("----------2------------")
start += 1
print("----------3------------")
else:
return "超出生成器的迭代范围"
try:
w = work(1,6)
for _ in range(60):
print(next(w))
# send不放在第一位,这个参数传递给yield作为yield的结果
print(w.send("abs"))
except StopIteration as a:
print(a.value)
输出结果:
----------1------------
1
abs
----------2------------
----------3------------
----------1------------
2
# 这是由于next造成的结果,所有yield没有值,只能返回一个None
None
----------2------------
----------3------------
----------1------------
3
abs
----------2------------
----------3------------
----------1------------
4
# 这是由于next造成的结果,所有yield没有值,只能返回一个None
None
----------2------------
----------3------------
----------1------------
5
abs
----------2------------
----------3------------
超出生成器的迭代范围
3、send 的结果是下一次调用 yield 时,yeid 后面的值。也就是说 send 是第二个,是下一次给 yield 的值,因为第一次是next,因为没有给 yield 值,所有返回的是None