迭代器
迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件
1 特点
- 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
- 不能随机访问集合中的某个值 ,只能从头到尾依次访问
- 访问到一半时不能往回退
- 便于循环比较大的数据集合,节省内存
2 迭代器的概念
迭代器协议:内部含有__next__
和__iter__
方法的就是迭代器
可迭代对象:内部有__iter__
方法就是可迭代对象。还有__next__
的话就是迭代器
迭代器协议和可迭代协议:
- 可以被for循环的都是可迭代的
- 可迭代的内部都有
__iter__
方法 - 只要是迭代器,一定是可迭代的
可迭代的.__iter__()
就可以得到一个迭代器迭代器的.__next__
方法可以一个一个的获取值
l = [1,2,3] # l 可迭代对象
print('__iter__' in dir(l)) # True
print('__next__' in dir(l)) # False
l = iter(l) # 可迭代对象通过调用iter()方法就能得到一个迭代器
print(l) # <list_iterator object at 0x0579DD30>
print('__iter__' in dir(l)) # True
print('__next__' in dir(l)) # True
for循环其实就是在使用迭代器。每循环一次,调用一次__next__
方法
只有是可迭代对象才能使用for循环。当我们遇到一个新的变量,如果无法确定是否可以使用for,可以查看这个变量是否有__iter__
方法,有就是可迭代的,可以使用for。
Iterator,可迭代对象,直接给出对象对应的内存地址。
3 迭代器的好处
- 从容器类型中取值,会把所有值都取到。
- 节省内存空间。 迭代器并不会在内存中再占用一大块内存,而是随着循环,每次生成一个(每次next就给一个)
生成器
一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器;
生成器本质就是 迭代器。
def func():
yield 1
yield 2
yield 3
yield 4
上述代码中:func函数称为生成器,当执行此函数func()时会得到一个迭代器。
>>> temp = func()
>>> temp.__next__()1
>>> temp.__next__()2
>>> temp.__next__()3
>>> temp.__next__()4
>>> temp.__next__()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration
从生成器中取值的几个方法:
next(生成器)
for循环
数据类型的强制转换 : 占用内存
1 生成器的表现形式
1 生成器函数
本质上就是我们自己写的函数。只要含有yield关键词的函数都是生成器函数。
# yield不能和return共用且需要写在函数内
l = [1,2,3,4,5]
def generator():
print(1)
yield 'a'
ret = generator() # ret 是执行之后得到的生成器
print(ret) # 打印的是生成器对象 <generator object generator at 0x04FCABB0>
print(ret.__next__())# 可以使用__next__方法
print(dir(ret))
#['__class__', '__del__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__iter__', '__le__',
'__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running',
'gi_yieldfrom', 'send', 'throw']
执行之后会得到一个生成器作为返回值
# 不同的生成器之间互不影响。
def wahaha():
for i in range(2000000):
yield '娃哈哈%s'%i
g = wahaha()
g1 = wahaha()
print(g.__next__()) # 0
print(g1.__next__())# 0
特点
-
调用函数的之后函数不执行,返回一个生成器。
-
每次调用next方法的时候会取到一个值。
-
直到取完最后一个,在执行next会报错 StopIteration
2 生成器表达式
g = (i for i in range(10)) # 注意:括号必须是()才返回生成器,括号不一样返回不同的值。几乎不占用内存
print(g)
for i in g:
print(i)
2 监听文件输入的案例
# 内容中有检索的字符,就输出对应行
def tail(filename):
f = open(filename,encoding='utf-8')
while True:
line = f.readline()
if line.strip():
yield line.strip()
g = tail('file')
for i in g:
if 'python' in i:
print('***',i)
3 案例
# 生成器实现:有一个文件,从文件里分段读取内容,在读出来的内容前面加上一个'***',再返回给调用者
def readfile(filename):
f = open(filename,encoding='utf8')
while True:
content = f.readline()
if content:
yield '***'+content
g = readfile('file')
for i in g:
print(i)
生成器函数进阶
send 获取下一个值的效果和next基本一致。只是在获取下一个值的时候,会给上个yield的位置传递一个数据
1 使用send的注意事项
第一次使用生成器的时候 是用next获取下一个值,最后一个yield不能接受外部的值
def generator():
print(123)
content = yield 1
print('=======',content)
print(456)
arg = yield 2
''''''
yield
# g1 = generator()
# g1.__next__()
# print('***',generator().__next__())
g = generator() #得到生成器
ret = g.__next__() # 打印123,执行第一个yield
print('***',ret) # *** 1
ret = g.send('hello') #send的效果和next一样,只是在获取下一个值的时候,会给上个yield的位置传递一个数据,此时 content = 'hello',继续向后运行,打印content,456
print('***',ret) # *** 2
2 获取平均移动值的案例
# 获取移动平均值
# 10 20 30 10
# 10 15 20 17.5
# avg = sum/count
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg
sum += num # 10
count += 1 # 1
avg = sum/count
avg_g = average() # 得到生成器
avg_g.__next__() # 此时执行第一个next方法,返回avg的值0
avg1 = avg_g.send(10)# 传递一个10给num,继续往后执行,此时num=10,sum=0+10,count=0+1,avg=10,循环继续,执行下一个yield。
avg1 = avg_g.send(20)# 再次传递一个20给num,继续往后执行,此时num=20,sum=10+20=30,count=1+1=2,avg=15,循环继续,执行下一个yield
print(avg1) # 此时avg1=15
3 预激生成器的装饰器 案例
def init(func): #装饰器
def inner(*args,**kwargs):
g = func(*args,**kwargs) #g = average()
g.__next__() #将第一次调用的next方法,放到了装饰器中
return g
return inner
@init
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg
sum += num # 10
count += 1 # 1
avg = sum/count
avg_g = average() #===> inner
ret = avg_g.send(10)
print(ret)
ret = avg_g.send(20)
print(ret)
小案例
yield from 等同于 for ...in...
def generator():
a = 'abcde'
b = '12345'
for i in a:
yield i
for i in b:
yield i
g = generator()
for i in g:
print(i)
# 结果:
a
b
c
d
e
1
2
3
4
5
def generator():
a = 'abcde'
b = '12345'
yield from a
yield from b
g = generator()
for i in g:
print(i)
# 结果:
a
b
c
d
e
1
2
3
4
5