迭代和可迭代协议
字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。
结合我们使用for循环取值的现象,再从字面上理解一下,其实迭代就是我们刚刚说的,可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代。
可迭代协
假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个一个的取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。
总结一下我们现在所知道的:可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。
迭代器协议
在for循环中,就是在内部调用了__next__方法才能取到一个一个的值。
那接下来我们就用迭代器的next方法来写一个不依赖for的遍历。
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
迭代器使用
list=[1,2,3,4]
a=iter(list) #创建迭代对象
print(next(a)) #1
print(next(a)) #2
print(next(a)) #3
list=[1,2,3,4]
a=list.__iter__() #创建迭代对象
print(a.__next__()) #1
print(a.__next__()) #2
print(a.__next__()) #3
print(a.__next__()) #4
print(a.__next__()) #报错
list=[1,2,3,4]
a=list.__iter__()
for i in a:
print(i)
生成器
我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是可以节省内存。
如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。
1.延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。
Python中提供的生成器:
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器Generator:
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
def func():
a=1
yield a
a=a+1
yield a
a=a+1
yield a
a=a+1
yield a
g=func() #创建迭代器
print(g.__next__()) #1
print(g.__next__()) #2
print(g.__next__()) #3
带输入的生成器
def func():
sum=0
count=0
avg=0
while True:
num = yield avg
count=count+1
sum=sum+num
avg=sum/count
g=func() #创建生成器
g.__next__()
print(g.send(1))#1.0
print(g.send(2))#1.5
print(g.send(3))#2.0
print(g.send(4))#2.5
带预激活的生成器(装饰器与生成器的组合)
def init(func): #在调用被装饰生成器函数的时候首先用next激活生成器
def inner(*args,**kwargs):
g = func(*args,**kwargs)
next(g)
return g
return inner
@init
def func():
sum=0
count=0
avg=0
while True:
num = yield avg
count=count+1
sum=sum+num
avg=sum/count
g=func() #创建生成器
print(g.send(1))
print(g.send(2))
print(g.send(3))
print(g.send(4))
yield from
def gen1():
for c in 'AB':
yield c
for i in range(3):
yield i
print(list(gen1()))#['A', 'B', 0, 1, 2]
def gen2():
yield from 'AB'
yield from range(3)
print(list(gen2()))#['A', 'B', 0, 1, 2]
列表推导式和生成器表达式
#老男孩由于峰哥的强势加盟很快走上了上市之路,alex思来想去决定下几个鸡蛋来报答峰哥
egg_list=['鸡蛋%s' %i for i in range(10)] #列表解析
#峰哥瞅着alex下的一筐鸡蛋,捂住了鼻子,说了句:哥,你还是给我只母鸡吧,我自己回家下
laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式
print(laomuji)
print(next(laomuji)) #next本质就是调用__next__
print(laomuji.__next__())
print(next(laomuji))
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:
sum(x ** 2 for x in range(4))
而不用多此一举的先构造一个列表:
sum([x ** 2 for x in range(4)])