列表生成式:
[i*2 for i in range(10)]
>> [0,2,4,6,8,10,12,14,16,18]
生成器
通过列表生成式可以直接创建一个列表,但当列表元素很多时,会占用很大的存储空间;所以,如果可以将列表元素按照某种算法推算出来,再循环过程中不断推算后续的元素,这样就不用创建完整的list ,节省了大量的空间,这种一边循环一变计算的机制,称为:生成器(generator)
生成器的创建
创建一个generator很简单,只需要将生成式的 [] 改为 () 即可:
[i*2 for i in range(10)]
(i*2 for i in range(10))
>> [0,2,4,6,8,10,12,14,16,18]
>> <generator object <genexpr> at 0x000000000580ABF8>
使用生成器此时,并未直接创建出列表,但如果使用循环调用这些数据:
a = (i*2 for i in range(10))
for i in a:
print(i)
>> 0
>> 2
>> 4
>> 6
>> ...
>> 18
#但如果直接调用某一位的数据:
a[3]
>> 出错
虽然开始时列表数据未被创建,但使用循环顺序调用时,数据都会生成出来;
如果不是顺序,而是直接调用某一位的数据,则此时会出错,因为它根本就没有被创建。
但是,如果推算的算法非常复杂,用for循环无法实现的时候,还可以用函数来实现,比如:斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
- 斐波拉契数列无法用列表生成式写出来,但可以用函数打印出来:
def fib(max):
n,a,b = 0,0,1
while n < max:
#print(b)
yield b
a,b = b,a+b
n = n+1
return "done" #调用超出时,会返回done异常
f = fib(100)
print(f)
print(f.__next__()) #依次打印
print(f.__next__())
print(f.__next__())
#每次得到一个值后,函数便停在yield处,此时可以转做别的事情,然后再回来还可以继续生成
>> <generator object fib at 0x00000000022B80F8>
>> 1
>> 1
>> 2
上述函数定义了 斐波拉契数列 的推算规则,从第一个元素开始,推出后续任意的元素
只需将print() 改为 yield 即变成生成器;
def fib(max):
n,a,b = 0,0,1
while n < max:
#print(b)
yield b
a,b = b,a+b
n = n+1
return "done"
g = fib(10)
while True:
try:
x = next(g)
print("g:",x)
except StopIteration as e:
print("Generator return value:", e.value)
break
>> g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
g: 13
g: 21
g: 34
g: 55
Generator return value: done
程序执行过程:
- **1、**While True 中执行try,调用 next ,则回到函数fib (next即取一位)
- 2、走到yield后,则中断,将b = 1的值带回来传给 x;(x=1),打印g:
- 3、然后再次执行try, 调用next ,再次回到函数fib
- 4、回到函数fib后,将从yield下一句继续执行
- 5、函数执行完(n = n+1执行完)后,再次跳回 yield
- 6、此时遇到 yield ,再次中断,回到打印g:
yield:相当于保存了此时函数的值。
生成器特点:
- 生成器只有在调用的时候才会产生相应的数据(只记住当前位置)
- 取值只能一个一个的取
生成器并行
函数执行了一半,停下来,可以做些别的事情,执行一会儿再回来,单线程也可以并发;
例:一个典型的生产者与消费者模型
import time
def consumer(name):
print("%s 准备吃包子了!" %name)
while True:
baozi = yield
print("包子[%s]来了,被[%s]吃了" %(baozi,name))
c = consumer("Evan")
c.__next__() #此时程序走到yield返回,后面的未执行
# c.send(b1) #把b1的值传到了yield ,赋值给了包子 。与__next__的区别是next只是调用yield,send调用yield并给它传值
# c.__next__()
def producer(name):
c = consumer("A")
c2 = consumer("B")
c.__next__()
c2.__next__()
print("我要开始做包子了!")
for i in range(10):
time.sleep(1)
print("做了一个包子,分两半")
c.send(i)
c2.send(i)
producer("Evan")
send和_ _ next _ _()的区别在于:
- next只是调用yield
- send调用yield并给它传值
程序运行流程可以打断点仔细看一遍;
迭代器
可直接作用于for循环的数据类型有:
- 数据集合类型:如 list、tuple、dict、set、srt等;
- generator,包括生成器和带yield的generator function这些可直接作用于for循环的对象统称为可迭代对象(Iterable),简单理解就是可循环的对象
判断:可以使用isinstance()来判断一个对象是否是Iterable对象
from collections import Iterable
isinstance([],Iterable) #判断列表是否为可迭代对象
>>True
迭代器和可迭代对象不是一回事
例如:
a = [1,2,3]
dir(a)
>> #一系列中没有__next__操作
from collections import Iterable #可迭代
isinstance(a,Iterable)
>True
from collections import Iterator #迭代器
isinstance(a,Iterator)
>>False
注意差别,可迭代和迭代器的差别
a 是一个可迭代对象,但 a 不是一个迭代器;
那如何将可迭代对象变成迭代器呢?
- 可以使用iter()函数
a = [1,2,3]
b = iter(a)
print(b)
b.__next__()
b.__next__()
isinstance(a,Iterator)
>> <list_irerator object at 0x000000000595EC18>
>> 1
>> 2
>> True
Iterator对象表示的是一个数据流,按需计算下一个数据,只有在需要返回下一个数据时才会进行计算,可以表示一个无限大的数据流;