· 迭代器
什么是迭代器:
迭代器可以让我们访问集合的时候变得非常方便。之前我们通过for…in…来访问一个集合的时候,就是使用迭代器完成的。
如果没有迭代器,那么我们只能通过while循环,每次循环的时候通过下标来访问了。
可迭代对象:
可以直接使用for循环遍历的对象,成为可迭代的对象,常见的可迭代对象有:list、tuple、dict、set、str以及生成器
更加专业的判断一个对象是否是可迭代对象:这个对象有一个__iter__方法,并且这个方法会返回一个迭代器对象,这种对象就叫做可迭代对象。
判断一个对象是否可迭代:
可以使用is instance()判断一个对象是否是interable对象
from collections import Iterable
# 列表是一个可迭代对象
ret = isinstance([1, 2, 3], Iterable)
print(ret)
字符串是一个可迭代对象
ret = isinstance('abc', Iterable)
print(ret)
整形不是一个可迭代对象
ret = isinstance(123, Iterable)
print(ret)
迭代器:
1、在python2中,实现了next和__iter__方法,并且在这个方法中返回了值的对象,叫做迭代器对象
2、在python3中,实现了__next__方法和__iter__方法,并且在这个方法中返回了值的对象,叫做迭代器对象
3、如果迭代器没有返回值了,那么应该在next或者是__next__方法中抛出一个StopInteration
使用iter()方法获取可迭代对象的迭代器:
有时候我们拥有了一个可迭代的对象,我们想要获取这个迭代器,那么可以通过iter(x)方法获取这个可迭代对象
的迭代器。
自定义迭代器:
分开的写法(方法A):
class MyRangeIterator:
"""
迭代器
"""
def __init__(self, start, end):
self.index = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.index < self.end:
temp = self.index
self.index += 1
return temp
else:
raise StopIteration()
class MyRange:
"""
MyRange是可迭代对象
"""
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
# 这个方法中返回一个迭代器对象
return MyRangeIterator(self.start, self.end)
if __name__ == '__main__':
myrange = MyRange(1, 10)
for i in myrange:
print(i)
iterator = iter(myrange)
while True:
try:
x = iterator.__next__()
print(x)
except StopIteration:
break
合并到一起的写法(方法B):
# -*- coding: UTF-8 -*-
from collections import Iterable, Iterator
# Interable: 判断是否可迭代
# Interator: 判断是否是迭代器
print(isinstance('abc', Iterable))
print(isinstance('abc', Iterator))
class MyRange:
def __init__(self, start, end):
self.index = start
self.end = end
def __iter__(self):
# 这个方法要返回一个迭代器
return self
def __next__(self):
if self.index < self.end:
temp = self.index
self.index += 1
return temp
else:
raise StopIteration()
if __name__ == '__main__':
myrange = MyRange(1, 10)
for i in myrange:
print(i)
for i in myrange:
print(i)
以上两个方法,A和B都实现了迭代器的功能,当我们实例化MyRange时,即获得了一个可迭代对象,在方法A中这个可迭代对象返回了一个迭代器:MyRangeIterator(self.start, self.end),方法B中返回了自己self本身。这时就会有一个区别产生,因为A方法在每次返回迭代器的时候,都会实例化迭代
器,而B中只会实例化一次,因此:
myrange = MyRange(1, 10)
for i in myrange:
print(i)
for i in myrange:
print(i)
如果两个方法都执行上述代码,A方法每次都会遍历出1到9,而B方法只会遍历一次,第二次什么都不会打印。因为第二次self.index 已经等于了 self.end。
· 生成器
为什么需要生成器:
当我们需要打印一个1到1亿的整形的时候,如果我们采用普通的方式,直接调用range函数,程序会崩溃掉。因为range(1,100000000)函数会直接产生一个从1-1亿的列表,这个列表中所有数据都是存放在内存中的,会导致内存爆满。这时候我们可以采用生成器来解决该问题,生成器不会一次把所有数据都加载到内存中,而是在循环的时候临时产生的,循环一次生成一个,所以程序在运行期间永远都只会生成一个数据,从而节省内存的开销。
next函数和__next__方法:
next函数可以迭代生成器的返回值
自己写生成器:
生成器可以通过函数产生。如果在一个函数中出现了yield表达式,那么这个函数将不再是一个普通的函数,而是一个生成器函数。yield一次返回一个结果,并且会冻结当前函数的状态。
普通的列表
num_list = [x for x in range(1, 100)]
print(num_list)
生成式
num_gen = (x for x in range(1, 100))
print(num_gen)
print(type(num_gen))
for i in num_gen:
print(i)
def my_gen():
yield 1
yield 2
yield 3
ret = my_gen()
print(next(ret))
print(next(ret))
print(next(ret))
print(next(ret))
def gen(start, end):
index = start
while index <= end:
yield index
index += 1
生成器有两个身份:迭代器和可迭代的对象, 因此,只能被遍历一次
ret = gen(1, 100000000)
for i in ret:
print(i)
send方法:
1、也是用来触发代码,直接碰到yield表达式
2、如果用send方法执行刚刚开始的生成器,那么应该传递None给send方法
def my_gen(start):
while start < 10:
# 如果通过next函数执行yield
# 那么yield xxx 永远都是返回None
temp = yield start
print(temp)
start += 1
ret = my_gen(1)
第一次使用send方法必须传None
print(ret.send(None))
print(next(ret))
print(ret.send(‘hello’))
1
None
2
hello
3
send方法和next函数的区别:
1、send方法可以传递值给yield表达式,而next不可以
2、在第一次执行生成器代码的时候,必须要传None,next则不需要
生成器的一些案例:
def fib(count):
index = 1
a, b = 0, 1
while index <= count:
yield b
c = b
b = a + b
a = c
index +=1
for x in fib(7):
print(x)
#使用生成器 来实现多线程,听音乐和看电影同时进行
def netease_music(duration):
time = 0
while time <= duration:
print('音乐听了%d分钟' % time)
time += 1
yield None
# raise StopIteration()
def youku_movie(duration):
time = 0
while time <= duration:
print('电影看了%d分钟' % time)
time += 1
yield None
# raise StopIteration()
if __name__ == '__main__':
music_iter = netease_music(10)
movie_iter = youku_movie(30)
music_stop = False
movie_stop = False
while True:
try:
next(music_iter)
except StopIteration:
print('音乐听完了')
music_stop = True
try:
next(movie_iter)
except StopIteration:
print('电影看完了')
movie_stop = True
if music_stop and movie_stop:
break