一、迭代器
迭代是Python最强大的功能之一,是访问集合元素的一种方式,是一个可以记住遍历的位置的对象。
实现了方法__iter__的对象是可迭代的对象,而实现了方法__next__的对象是迭代器, 像序列和字典 都是可迭代的,因为他们都实现了__iter__方法。
1.1 创建一个迭代器
一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 。
__iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
__next__() 会返回下一个迭代器对象。
例子:
创建一个字符串列表,每个元素从'hello-10' 到 'hello-50' ,步长为2
class MyNumbers:
def __init__(self,prefix,startindex,endindex,step):
self.prefix=prefix
self.startindex = startindex
self.endindex = endindex
self.step = step
def __iter__(self):
self.a=self.startindex
return self
def __next__(self):
if self.a <= self.endindex:
x = self.a
self.a += self.step
return self.prefix+str(x)
else:
raise StopIteration
myclass = MyNumbers('hello',10,50,2)
myiter = iter(myclass)
mylist = []
for x in myiter:
mylist.append(x)
print(mylist)
因为MyNumbers实现了__iter__、__next__,所以可以直接通过 list() 函数将,生成列表
myclass = MyNumbers('hello',10,50,2)
mylist=list(myclass)
print(mylist)
1.2 iter() 和next()
迭代器有两个基本的方法:iter() 和 next()
字符串,列表或元组对象都可用于创建迭代器:
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
print(next(it)) # 输出迭代器的下一个元素
print(next(it)) # 输出迭代器的下一个元素
class MyNumbers:
def __init__(self,prefix,startindex,endindex,step):
self.prefix=prefix
self.startindex = startindex
self.endindex = endindex
self.step = step
def __iter__(self):
self.a=self.startindex
return self
def __next__(self):
if self.a <= self.endindex:
x = self.a
self.a += self.step
return self.prefix+str(x)
else:
raise StopIteration
myclass = MyNumbers('hello',10,50,2)
myiterobj=iter(myclass)
print(next(myiterobj))
print(next(myiterobj))
二、生成器
根据自己制定规则循环生成数据,当条件不成立时则生成数据结束。数据并不是一次性全部生成处理,而是使用一个,再生成一个,这样可以节约大量的内存。
生成器有2中创建方式:
yield 表达式
生成器推导
2.1 yield 表达式
生成器是包含关键字yield的函数,但被调用时不会执行函数体内的代码,而是返回一个迭代器。每次请求值时,都
将执行生成器的代码,直到遇到yield或return。 yield意味着应生成一个值,而return意味着生成器应停止执行。
def MyNumbers(prefix,startindex,endindex,step):
x = startindex
while x <=endindex:
yield prefix + str(x+step)
x = x +step
mylist = []
mygenerator=MyNumbers('hello-',10,50,2)
for x in mygenerator:
mylist.append(x)
print(mylist)
代码执行到 yield 会暂停,然后把结果返回出去,下次启动生成器取值时会在暂停的位置继续往下执行
同样也可以使用 mylist=list(MyNumbers) 生成列表
2.2 生成器推导式:
与列表推导式类似,只不过生成器推导式使用小括号
mylistgenerator = ('hello-'+str(x) for x in range(10,50,2))
print(mylistgenerator) #<generator object <genexpr> at 0x000001D8F0AE6C48>
#mylist=list(mylistgenerator)
mylist =[]
for x in mylistgenerator:
mylist.append(x)
print(mylist)
可使用next 函数获取生成器中的下一个值,for 循环遍历生成器中的每一个值
三、生成器方法
生成器方法主要有三种:
send: yield 向外界提供一个值,同样外界可以向生成器发送一个值,该值位于 yield 左边
throw: 在生成器函数执行暂停处,抛出一个指定的异常,之后程序会继续执行生成器函数中后续的代码,直到遇到下一个 yield 语句。如果到剩余代码执行完毕没有遇到下一个 yield 语句,则程序会抛出 StopIteration 异常。
close: 在生成器函数暂停的地方抛出一个 GeneratorExit 异常
3.1 send
def MyNumbers(prefix,startindex,endindex,step):
x = startindex
while x <=endindex:
y = yield prefix + str(x+step)
x = x + step
print(f'Get string:{y}')
mylist = []
mygenerator=MyNumbers('hello-',10,50,2)
onestring= mygenerator.__next__() #第一次取值后, 停留在 yield prefix + str(x+step) 即 yield 'hello-' + str(10+2), onestring为 hello-12
print(mygenerator.send(onestring)) #send 发送后,执行 y='hello-12' ; x=x+2 ; print(f'Get string:{hello-12}') ; yield prefix + str(x+step) 即 yield 'hello-' + str(12+2); 返回了 'hello-14'
onestring= mygenerator.__next__() #此时__next__相当于执行了 send(None). y = none ; x=x+2; print(f'Get string:{None}'); yield prefix + str(x+step) 即 yield 'hello-' + str(14+2); 返回了 'hello-16'
print(mygenerator.send(onestring)) #send 发送后,执行 y='hello-16' ; x=x+2 ; print(f'Get string:{hello-16}') ; yield prefix + str(x+step) 即 yield 'hello-' + str(16+2); 返回了 'hello-18'
通过调用 next()或者 __next__() 方法,可以实现从外界控制生成器的执行。send() 方法可带一个参数,也可以不带任何参数(用 None 表示)。其中,当使用不带参数的 send() 方法时,它和 next() 函数的功能完全相同。
3.2 close方法
def MyNumbers(prefix,startindex,endindex,step):
x = startindex
while x <=endindex:
try:
yield prefix + str(x+step)
x = x + step
except GeneratorExit :
print('生成器结束')
break
mylist = []
mygenerator=MyNumbers('hello-',10,50,2)
onestring= mygenerator.__next__()
print(onestring)
onestring= mygenerator.__next__()
print(onestring)
mygenerator.close()
在while 循环异常处理必须加上break 跳出循环,否则循环再一次走到 yield prefix + str(x+step), 此时生成器已经关闭,将产生RuntimeError异常
异常这代码中不能再包含 yield 语句,否则程序会抛出 RuntimeError 异常。 也无法再调用 next()函数或者 __next__() 方法启动执行,否则会抛出 StopIteration 异常。
3.3 throw方法
generator.throw(type[, value[, traceback]])
def MyNumbers(prefix,startindex,endindex,step):
x = startindex
while x <=endindex:
try:
yield prefix + str(x+step)
x = x + step
except ZeroDivisionError:
print('捕获到了 异常 ZeroDivisionError')
except GeneratorExit :
print('生成器结束')
break
mylist = []
mygenerator=MyNumbers('hello-',10,50,2)
onestring= mygenerator.__next__()
print(onestring)
onestring= mygenerator.__next__()
print(onestring)
print(mygenerator.throw(ZeroDivisionError))
onestring= mygenerator.__next__()
print(onestring)
mygenerator.close()
#生成器结束
从 yield prefix + str(x+step) 处发送了异常,此时返回时 是上次的值 hello-14, 此时暂停在这里.
当 执行最后的onestring= mygenerator.__next__(), 将继续执行x=x+step; while x<=endindex; yield prefix + str(x+step) 此时 取得hello-16, 最后关闭生成器
四、总结
可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list,tuple,dict,set,str等
一类是generator,包括生成器 和带yield的generator function
可以直接作用于for 循环的对象统称为可迭代对象:Iterable
可作用于next()函数的对象都是Iterator类型;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
通过iter函数将可迭代对象转换为迭代器
可以使用isinstance()判断一个对象是否为可Iterable对象
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False