Python17 生成器

列表生成式与生成器详解

1.列表生成式(推导式列表)

image_1c0alik4m1s78vc4i7uegq9q19.png-4.5kB
通过for i in range(10)来循环0-9折10个数字,然后将每一个数字赋值给i,i在乘以2得出图中下面的结果。

a = []

for i in range(10):
    a.append(i*2)

print (a)

image_1c0alj7fkttnuv7121g39318nfm.png-1.7kB
结果是一样的

a = [i * 2for i in range(100)]
print (a)

image_1c0alk7pdjq147j1eon71gs0p1j.png-11.2kB
列表生成式所生成的这些数据是会被放入到内存中的(除非删除或执行程序结束),可以随调用其中的任意数据。

print (a[10])

image_1c0alkpkv1qv1f3okn31g1c1u1220.png-0.5kB
可以看到可以直接print (a10)来取值,能直接取值说明这个值是一直保存在内存中的,如果这个值元素的数量少到不会占很多资源,但是假如元素有一千万,一个亿时就会占用很多内存空间了。

2.生成器

c = (i * 2for i in range(100))
print (c)

image_1c0allrgspeb19js1ktmc3fovg2d.png-3.6kB
可以看到,只是将中括号变成小括号后,输出的只是一个生成器,这个生成器当前没有任何的数据放在内存中,只有被调用的时候才会在内存中生成,结束后就删除内存中的元素,不调用的话就不会一直占用内存空间。

print (c[10])

image_1c0almh7d1aa8s841li9rn31sjt2q.png-11.5kB
可以看到使用print (c10)去引用内存中的数据并不存在,因为生成器没有被调用,所以没有任何数据元素存在内存中,所以这里会报错。

for i in c:
    print (i)

image_1c0aln66r1i8j1snm9rj1a22opm37.png-4.9kB

通过for来调用c,赋值给i,在打印i,这时候就可以看到一个个的数字被新生成

print (c[10])   #生成结束后我们在试着去引用c的元素,可以看到下图依然报错,这说明生成器被调用结束后,生成的元素值会在内存中删除,不会一直占用内存空间。

image_1c0alnv4onseog11gp5maa1jrr3k.png-12kB

c = (i * 2for i in range(100))
print (c.__next__())
print (c.__next__())
print (c.__next__())

image_1c0aloif0l4bk518ie7ig4o641.png-0.8kB
可以通过c.next()来不断的取c的下一个值,第一个值0,第二个2,以此类推来生成上一个值的下一个值。

下面我们在pycharm的python console中测试,这样取值能更方便一些
image_1c0alpb531qh82e1dqqpqrmik4e.png-5.7kB

image_1c0alpfl71jli16aa168mf0e1j114r.png-6.2kB
循环到50630后,这里使用了ctr+c强制停止了

image_1c0alpp3q1pr71vgaqj23jep4958.png-4.2kB

然后通过c.next()可以看到,可以继续在中断值的位置继续获取下面的值。

当前已经生成了50634这个值,之前生成的值都已经没有了,被删除了,而且后面的值还没有生成,所以生成器只会记住当前的数字

image_1c0alq30srsmjki1jbe1dae1ejl5l.png-0.9kB

image_1c0alqtp91c5717gd1a1eidc94r62.png-7.1kB

image_1c0alr2jfsop115otlp13fois6f.png-2kB

可以看到生成过的数字当前并不存在,说明已经被删除掉了,生成器只记住了当前的值。

所以生成器并不会大量的去占用内存,同一时间只会存在一个值,所以有大量数据的时候,生成器是很常用的。

在3.X版本中使用__next__(),在2.7中使用next()

使用__next__()只能一个一个的去生成,所以一般都常使用for来循环生成。

3.函数定义生成器

斐波那契数列:除了第一个和第二个数外,任意一个数都可以由前两个数相加得到:
image_1c0alrsaf1m9el743doj3b6u06s.png-10.7kB
有规律,可以被推到出来的数字

image_1c0als6us1bb6vatuta1gc91g7279.png-10.9kB
print (b)下面的 a,b = b,a+b 相当于 0,1 = 1,0+1,这里的a+b的a其实是引用了while上面的b,因为当前代码并没有执行完成所以a不会是1。

前两位相加a+b=0+1,然后是1+1=2, 1+2=3, 2+3=5.....

image_1c0alsqjn1ten1lecbto2v21t1s7m.png-2.3kB
目前还不算是一个生成器

image_1c0althuq1s2518ohfum7mhtat8j.png-15.9kB

可以看到将print改成yield 就算是一个生成器了,只有生成器可以使用 f.__next__()

image_1c0altol119uud5j1h8p159s13f90.png-5.2kB
通过next调用一次,返回一个值

image_1c0alu20h1vq11rai1sf2gmk1sd39d.png-17.7kB

image_1c0alu7l6163r143q16iutkg1gm59q.png-7.9kB

可以看到for循环是继续3来之后来循环的。

这里我们并没有看到return返回的done,因为for循环无法打印函数的返回值。

image_1c0aluhmj2jt1n9m6k41eckf73a7.png-22.7kB

image_1c0alumst1be96bj149qifcsqrak.png-18.2kB

不断使用next可以看到错误信息和其后面的done

在斐波那契中我们可以大概定位有多少个数字,但是如果是在其他情况和场景下,我们不知道有多少个,且我们使用next超过了这些实际的个数就会报错,比如我们在调用函数时fib(10),只赋予了10个,但是我们使用next却超过了10个,这里就会报错。

image_1c0alv33p1vk41umq1sgu1i2k1591b1.png-29.1kB
通过try一直print ('f:',x) 直到抓取到StopIteration 这个错误 将其设为别名e,然后执行该except下面的代码
image_1c0alvd7m1e701g2i124510jspa5be.png-6.5kB

StopIteration这个错误信息就是相当于通过不断的print ('f:',x) 来发现的,这个本身就代表了 上一个代码中的StopIteration:done,只不过这里将其e作为别名,而e.value,只是StopIteration:done中的这个done,所以最后打印的是Generator return value: done。

image_1c0am0kil1a3l191vgeo120010gsbr.png-48.4kB
使用断点来查看步骤:

从第5步,调用函数就会返回到生成器函数,在返回之前会保留该位置,一直到第8步会将b当前的值传给第9步的x(在传之前会保留yield这个位置,保留中断状态,下次返回继续),第9步打印x,因为还没有try完成,所以到了第10步,然后11步又返回生成器函数(返回的是上次保留的位置),因为是while循环所以到了第14步,第14步又到了15步,这样后续以此类推。

yield的作用可以保留当前位置状态,将值返回出去

生成器可以实现单线程中并发(协程),速度比多线程还要快。
image_1c0am14rr1m9665t12njst6esec8.png-40kB
send与next相同,都是用来唤醒yield,只不过send还会传值
image_1c0am1ili1bgt47sp1e1qmt1enicl.png-4kB

image_1c0am1v3mr6c1evnr0gkmfhed2.png-19.9kB

image_1c0am276ekd8ta34df14rknp3df.png-22.8kB

异步

转载于:https://blog.51cto.com/daimalaobing/2046674

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值