前言:
眼前我们对“js 生成器函数”大体比较注意,咱们都想要剖析一些“js 生成器函数”的相关知识。那么小编也在网摘上汇集了一些有关“js 生成器函数””的相关文章,希望姐妹们能喜欢,我们一起来学习一下吧!Hello!大家好,我是BugBear,一个专注于分享软件测试干货的测试开发。今天我们来学习一下Python的生成器。
一、生成器定义
在Python中,一边循环一边计算的机制,称为生成器:generator
二、生成器作用
列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间
我又想要得到庞大的数据,又想让它占用空间少,那就用生成器
三、生成器创建
生成器的创建有两种方式:
第一种方法:只要把一个列表生成式的[]改成(),就创建了一个generator
# 列表生成式转为生成器print((i for i in range(9)))<generator object <genexpr> at 0x10fc137d0>
第二种方法:如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象
data_list = [i for i in range(9)]def test(): k=0 while True: if k <= len(data_list): j = yield 5 # yield必须用在函数中 print(j+k) k+=1 else: breakprint(test())<generator object test at 0x1070b7750>
四、生成器工作原理
第一、生成器(generator)通过重复调用next()方法,直到捕获一个异常
第二、带有 yield 的函数不再是一个普通函数,而是一个生成器generator。可用next()调用生成器对象来取值。next 两种方式 t.__next__() | next(t)。 可用for 循环获取返回值(每执行一次,取生成器里面一个值)(基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)
第三、yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行,第一次遇到yield时,返回的会是yield右边的值
data_list = [i for i in range(9)]def test(): k=0 while True: if k <= len(data_list): j = yield 5 # yield必须用在函数中 print(j+k) k+=1 else: breaka = test()print('第一次:{}'.format(a.__next__())) # 5print('第二次:{}'.format(a.__next__())) # exception报错
当运行到yield时,第一次直接返回右边的值5给调用者a,并记录返回的位置,后面的代码则不会运行,下次迭代时,会直接从print(j+k)开始运行,但是此时j是没有赋值的,是一个NoneType,所以j+k就引发报错
Traceback (most recent call last): File "/Users/Desktop/demo/learn/rt001/20210121/demo001.py", line 24, in <module> print('第二次:{}'.format(a.__next__())) File "/Users/Desktop/demo/learn/rt001/20210121/demo001.py", line 16, in test print(j+k)TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'第一次:5
第四、.send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果。换句话说,就是send可以强行修改上一个yield表达式值。
data_list = [i for i in range(9)]def test(): k=0 while True: if k <= len(data_list): j = yield 5 # yield必须用在函数中 print(j+k) k+=1 else: breaka = test()print('第一次:{}'.format(a.__next__()))print('第二次:{}'.format(a.send(10)))print('第三次:{}'.format(a.send(10)))# 结果第一次:510第二次:511第三次:5
我们针对上面实例进行分析:
可以看到test函数中有一个 j=yield 5,第一次迭代到这里会返回5给调用者也就是a,j还没有赋值则为NoneType,此时记录返回的代码位置。
第二次迭代时,使用a.send(10),则从之前记录的代码位置的下一行代码开始运行,也就是print(j+k)。强行修改yield 5表达式的值为10并赋值给了j,那么j =10。如果不是用a.send()而使用a.__next__()的话,那么则会因为j为NoneType引发报错。
下面我们来看看官方的解释吧
generator.__next__() 开始一个生成器函数的执行或是从上次执行的 yield 表达式位置恢复执行。 当一个生成器函数通过 __next__() 方法恢复执行时,当前的 yield 表达式总是取值为 None。 随后会继续执行到下一个 yield 表达式,其 expression_list 的值会返回给 __next__() 的调用者。 如果生成器没有产生下一个值就退出,则将引发 StopIteration 异常。
此方法通常是隐式地调用,例如通过 for 循环或是内置的 next() 函数。
generator.send(value) 恢复执行并向生成器函数“发送”一个值。 value 参数将成为当前 yield 表达式的结果。 send() 方法会返回生成器所产生的下一个值,或者如果生成器没有产生下一个值就退出则会引发 StopIteration。 当调用 send() 来启动生成器时,它必须以 None 作为调用参数,因为这时没有可以接收值的 yield 表达式。
通过实例解析:
1、当运行a.__next__()时,遇到yield 5时,直接return 5,则打印 第一次: 5
2、当运行a.send(10)时,会将之前yield 5表达式的结果直接变为10,并赋值给j,然后从print(j+k)开始执行代码,直到遇到下一次的yield,此时打印 10 和 第二次: 5,后面以此类推
五、yield使用
yield相关我们常用的有next,send,__next__,close等
next和__next__的作用就是按需取下一个值close就是销毁了这个生成器send的作用是给yield的左边传参
当然第一次调用的时候,因为yield的执行顺序是先右边再左边,所以,我们遇到yield就停止了运行,此时send是无法给左边赋值的,那我们第一次调用只能send(None)
def gen_test(): value = 0 while True: s = yield value print('s=', s) value += 1 m = gen_test()m.send(None) # 第一次启动生成器时需要给Nonefor i in range(10): m.send(i)
结果如下
s= 0s= 1s= 2s= 3s= 4s= 5s= 6s= 7s= 8s= 9
这里我们每次有个变量s,它是用来接收send传值的。
至于为什么第一次使用None进行传值,是因为第一次我们遇到yield就会暂停。而并没有进行赋值操作。所以我们传None
OK,看输出结果。确实是在第二次send的时候我们才能接收到send的值,而第一次必须传None,我们从他的祖先去看看
Sending a value into a new generator produces a TypeError:>>> f().send("foo")Traceback (most recent call last):...TypeError: can't send non-None value to a just-started generator
OK,看了下它的底层,确实是必须在一个生成器第一次迭代的时候必须传None
六、yield from用法
yield from简单实现
yield是每次“惰性返回”一个值,其实从名字中就能看出,yield from 是yield的升级改进版本,如果将yield理解成“返回”,那么yield from就是“从什么(生成器)里面返回”,这就构成了yield from的一般语法,即 yield from generator
从上面可以看出,yield from 后面可以跟的可以是生成器 、元组、 列表、range()函数产生的序列等可迭代对象
简单地说,yield from generator 。实际上就是返回另外一个生成器。而yield只是返回一个元素。
从这个层面来说,有下面的等价关系:
yield from iterable# 本质上等于for item in iterbale: yield item
yield是直接生成,而yield from则是从子生成器里生成。因为它具有迭代的性质,如果我们还是可以通过迭代的方式来输出
def gen_from(*args): for items in args: for item in items: yield item
简化为
def gen_from(*args): for item in args: yield from item
那我们找一个简单的实例来看看吧
data = []data_list = [x for x in range(9)]data.append(data_list)def test(data): for i in data: yield from i # 从data_list中迭代返回valuea = test(data)print(a.__next__())print(a.__next__())print(a.__next__())
结果为
012
标签: #js 生成器函数