一、生成器
1、生成器
前面一章我们说过列表生成器;
li = [i for i in range(30) if i%2==0]
得到下面的结果;
我们遍历1~30,找出其中的所有偶数,输出的结果为一个列表;
如果我们用生成式再呈现一次呢;
g = (i for i in range(100) if i%2==0)
我们能看到,生成器的返回值不在是 一个列表,而是一个生成器,我们不能直接打印,而是需要去遍历这个生成器;
2、查看生成器内容的两种方式
方式一:注意:在python2中,我们用g.next();python3中 g.__next__方法( ) 或 next(g)
python2.x中:
python3.x中:
next( ) 每次只执行一次,并且等待下一次的指令;生成器并不会结束
方式二:for循环
g = (i for i in range(30) if i%2==0) for i in g: print(i,end=',')
方式三:
g = (i for i in range(30) if i%2==0) while True: try: print(g.__next__(),end=',') except StopIteration: break
这里我们再强调一次,怎么去看一个对象是否为可迭代对象:
from collections import Iterable g = (i for i in range(30) if i%2==0) print(isinstance(g, Iterable))
3、yield关键字
当在函数中看到yield关键字, 那么这个函数调用的返回值是一个生成器;
Yield的用法有点像return;
1)函数中使用yield,可以使函数变成生成器。一个函数如果是生成一个数组,就必须把数据存储在内存中,如果使用生成器,则在调用的时候才生成数据,可以节省内存。
2)生成器方法调用时,不会立即执行。需要调用next()或者使用for循环来执行。使用for循环不需要自己捕获StopIteration异常。使用next()方法,当生产器方法执行结束会抛出StopIteration异常(只要不是使用yield返回数据,都会抛出StopIteration异常)。
3)yield不仅可以返回值,也可以接收值。
4)调用生成器send方法传递数据时,必须先调用next(c)或者c.send(None)方法,执行到yield语句,等待接收数据。否则会报错。
例:
def fib(num): a, b, count = 0, 1, 1 # 0, 1 while count <= num: yield b a, b = b, a + b #a=2, b=3 count += 1 g = fib(10) for i in g: print(i,end=',')
例2、
def fun(): a = "world" print("hello") print(1) yield 2 print(3) yield 4 g = fun() print(g) print(g.__next__())
通过上面两张图片的对比,我们发现生成器在被调用时,首先他会运行至yield关键字这里,并且在这里等待下一次的调用,并不会结束函数;直到我们第二次调用时自动到下一个yield关键字继续等待,或运行至函数结束。
二、生产者、消费者模型
例:下面以简单的包子的制作与购买制作模型;
def consumer(name): print("%s准备买包子....." %(name)) while True: kind = yield print("%s已经购买%s口味包子....." %(name, kind)) def producer(name): c1 = consumer("张三") c2 = consumer("李四") c1.__next__() c2.__next__() print(c1 is c2) print("厨师%s准备制作包子......" %(name)) for kind in ['酸菜', '牛肉','香菇青菜','豆腐粉条']: time.sleep(random.random()) #导入时间模块,让时间在这里随机的停0~1秒,模拟包子制作的过程; print("%s制作了%s口味的包子" %(name, kind)) c1.send(kind) c2.send(kind) producer(' 我 ')
这里我们要着重说明一下yield 和 send,我们先用一个一段代码理解:
def MyGenerator(): v = yield 1 # yield:语句执行到yield就停止了 所以刚开始运行到这一句的时候v没有赋值为1,等到send(5)时(yield 1被视为传入的参数5) 此时这一句被执行,v=5 v = yield v b = yield v v = yield v c = yield b gen = MyGenerator() print(next(gen)) print(gen.send(5)) # v = yield 1 ->v = 5 ,先yield v,未执行v = 这里的赋值语句 print(gen.send(7)) # v = yield v ->v = 7 ,先yield v,未执行b = 这里的赋值 print(gen.send(5)) # b = yield v ->b = 5 ,yield v(=7) ,未执行v = 这里的赋值 print(gen.send(7)) # v = yield v ->v = 7 ,yield b(=5) 结束
小作者本人也还在研究之中,yield和send的理解和使用方法,这个问题先放在这里,待我搞清楚再回来作补充.........
三、协程
协程是一种允许在特定位置暂停或恢复的子程序——这一点和生成器相似。但和生成器不同的是,协程可以控制子程序暂停之后代码的走向,而生成器仅能被动地将控制权交还给调用者。
例:假设有两个子程序main和printer。printer是一个死循环,等待输入、加工并输出结果。main作为主程序,不时地向printer发送数据。
线来看一段代码
def printer(): counter = 1 while True: text = yield print('[%d] %s' %(counter, text)) counter += 1 def main(): p = printer() next(p) for i in range(10): p.send('张三') p.send('李四') p.send('王麻子') main()
printer函数做一个子函数,yield这里理解为等待send传参数,当main函数传入‘张三’这个参数时,printer函数开始进入循环,直到‘张三’这个参数10次传输完成截至,接着到下一个send=‘李四’传入printer函数........以此类推直到最后一次‘王麻子’传参结束。整个循环全部结束。
注意:我们在使用yield 和 send时,在send语句前一定要使用next(函数名),否则系统会报错;
四、迷你机器人
我们一般使用的机器人对话都是由迷你型机器人衍生出来的,没有真正意义上的实现科幻电影中的那些只能场景;
def robot(): say = '' while True: s = yield say if 'age' in s: say = '爸爸25了' elif 'name'in s: say = '求我阿,求我我就告诉你' elif 'birthday' in s: say = '明天就是爸爸的生日啦' elif 'hello'in s: say = 'hello too' else: say = '我什么都不知道!' def main(): g = robot() next(g) while True: user_send = input('我:') robot_send = g.send(user_send) print('robot:%s'%(robot_send)) main()
今天早上2018/05/10,我看到谷歌推出了一款真正的交互式机器人,抛弃了传统的siri,真正意义上的称的上为人工智能,现场演示:发布会上,用户对Google Assistant说:我想剪头发。Google Assistant接到指令后直接帮你打电话预约!!!........
看完了整场演示我为之震惊,人工智能已经在我们的身边潜伏着,随着时代的发展也许一个不留神你就被机器人无情的踩在脚下;还记得那次人机大战吗?阿尔法和李世石的围棋大战,当天阿尔法输掉了比赛,疲惫的李世石早已睡下,世界都以为人工智能不过是一场虚惊。然而,当天晚上阿尔法和自己下了一百万盘围棋。当第二天太阳升起,李世石还是那个李世石,但是阿尔法早已有了天翻地覆的变化。废话先写这么多吧,我的感受颇深,毕竟这里谈论的是技术,就不多作感情上的宣泄李。