python生成器

#在了解生成器之前,可以先看看一个名为"列表生成式"的东西

list = [ x*2 for x in range(0,9) ]
print(list)
print(type(list))

########
[0, 2, 4, 6, 8, 10, 12, 14, 16]
<class 'list'>

#由名字可知,列表生成式的作用就是生成一个列表,规则是: [ f(x) for x in 序列 ]

#但是,列表声称式会将生成的列表直接放入内存中,当数据较大时不适用

 

#所以就有了生成器

#生成器的第一种创建方法:( f(x) for x in 序列 )

s1 = ( x*3 for x in range(0,3) )
print(s1)

########
<generator object <genexpr> at 0x0000000001E8C518>

#发现直接打印s1无法获取其中的信息,输出的是 生成器对象和其地址

#这就是生成器与列表生成式的区别所在,生成器不会直接将所有数据放入内存中,而是作为一个对象返回

#当需要使用时,通过next()方法调用

print(next(s1))
print(next(s1))
print(next(s1))
print(next(s1))

########
0
3
6
Traceback (most recent call last):
  File "E:/9--python/python_project/test.py", line 25, in <module>
    print(next(s1))
StopIteration

#生成器在创建时就已经确定了其数据量,调用的次数就已经确定,如果超过生成器的数据量的调用就会有  StopIteration  的报错,此报错在for循环中会用到,当然这是后话

#由此可见,生成器占用的内存仅为保存对象地址,在调用时才生成值

#第一种生成器的创建比较粗旷,另外一种生成器的创建则可定义的内容较多

def foo():
    print("ok1")
    yield 2
    print("ok2")
    yield 5
s2=foo()
print(s2)
print(next(s2))
print(next(s2))
print(next(s2))

########
<generator object foo at 0x00000000027CC518>
ok1
2
ok2
5
Traceback (most recent call last):
  File "E:/9--python/python_project/test.py", line 37, in <module>
    print(next(s2))
StopIteration

#生成器也算是函数的另一变种,一个yield就可以迭代一次,yield的返回值同return一样可以用参数接收

#在yield中,自动封装了next()方法,使得我们可以优雅的调用

#next()方法的路径是:

1.第一个next(s1),进入foo()函数体,遇到print("ok1"),输出

2.向下执行代码,遇到yield 2,返回指定值2,退出,状态保存在此,待下一个next()执行来调用

3.第二个next(s2),进入foo()函数体,从yield 2开始,向下执行代码,遇到print("ok2"),输出

4.向下执行代码,遇到yield 5,返回指定值5,退出,状态保存在此,待下一个next()执行来调用

5.第三个next(s3),进入foo()函数体,从yield 5开始,向下执行代码,因为没有yield,报错

#从以上可知,生成器的代码不会一次性执行完,而是遇到yield就退出,并将状态保存,待下次执行时调用,这个是实现协程的基础

#生成器除了next()方法外,还有一个可以传入参数的方法:generator.send()

def bar():
    count1 = yield 5
    print(count1)
    count2 = yield 1
    print(count2)
s3=bar()
ret1=s3.send(None)
print(ret1)
ret2=s3.send(2)
print(ret2)
ret3=s3.send(6)
print(ret3)

########
5
2
1
6
Traceback (most recent call last):
  File "E:/9--python/python_project/test.py", line 52, in <module>
    ret3=s3.send(6)
StopIteration

#generator.send()方法可以向生成器传入参数,但第一次因为还没有进入函数体,所以也不知道传递给那个参数,所以第一次使用send()方法时不能传入值,generator.send(None)=next(generator)

#当下一次使用send(var)方法传入值时,会进入上次退出的位置,将值传给此处预先定义的参数,向下执行代码,直到下一个yield,保存状态,退出

posted @ 2018-03-27 15:11  jianbonet  阅读(204)  评论(0编辑  收藏  举报