您的位置:首页 > 编程语言 > Python开发

Python——迭代器和生成器

2016-10-19 21:53 441 查看
一、迭代器主要用于遍历操作,在iter方法中会返回一个迭代器,该迭代器包含next方法,在调用next方法时,迭代器返回它的下一个值。如果next方法被调用,但是迭代器没有值可以返回时,出发StopIteration异常。下面是一个求斐波那契数列的例子:

# _*_ coding:utf-8 _*_
class Fibs:
def __init__(self):
self.a=0
self.b=1
def __next__(self):
self.a,self.b=self.b,self.a+self.b
return self.a
def __iter__(self):
return self

fibs=Fibs()
for f in fibs:
if f > 1000:
print(f)
break

运行结果:
==========RESTART: C:\Users\Mr_Deng\Desktop\test.py==========
1597
>>>


通过迭代可以自定义一个序列,如下:

# _*_ coding:utf-8 _*_
class testList:
value=0
def __init__(self,n=10):
self.n=n
def __next__(self):
self.value+=1
if self.value > self.n:
raise StopIteration
return self.value
def __iter__(self):
return self

运行结果:
==========RESTART: C:\Users\Mr_Deng\Desktop\test.py==========
>>> l=testList(20)
>>> list(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
>>>


二、生成器:通过普通的函数语法定义的迭代器,其创建如下:

# _*_ coding:utf-8 _*_
nested=[[1,2],[3,4],[5]]
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element

运行结果:
==========RESTART: C:\Users\Mr_Deng\Desktop\test.py==========
>>> for num in flatten(nested):
print(num)

1
2
3
4
5
>>> list(flatten(nested))
[1, 2, 3, 4, 5]
>>>


包含yield语句的函数成为生成器,通常使用print语句进行输出,而使用yield语句可以是产生的值冻结,即函数会在冻结点等待激活,函数激活后会在停止的点开始执行。

创建的一般生成器只能处理两层循环,但是若要处理三层或者更多层数的循环需要使用递归生成器。

# _*_ coding:utf-8 _*_
__metaclass__=type
def flatten(nested):
try:
for sublist in nested:
for element in flatten(sublist):
yield(element)
except TypeError:
yield nested

运行结果:
==========RESTART: C:\Users\Mr_Deng\Desktop\test.py==========
>>> x=([1,2,3,[4,5],[1,2,[6,7]]])
>>> x
[1, 2, 3, [4, 5], [1, 2, [6, 7]]]
>>> list(flatten([1,2,3,[4,5],[1,2,[6,7]]]))
[1, 2, 3, 4, 5, 1, 2, 6, 7]
>>>


Flatten函数调用时会存在两种可能的情况,一种是需要递归展开,另一种是不需要递归展开。在不要递归展开的情况中,函数被告知展开一个元素,这种情况下,for循环会引发TypeError,生成器也会产生一个元素。对于需要展开的列表或者其他可以迭代的对象,函数会遍历所有子列表中的所有元素,并对他们再次调用flatten函数,然后在调用函数中通过循环来产生被展开自列表的所有元素。但是,递归生成器不能对字符串对象(包括字符串、Unicode、UserString等)进行迭代,这样导致出错。

>>> list(flatten('12345'))
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
list(flatten('12345'))
File "C:\Users\Mr_Deng\Desktop\test.py", line 6, in flatten
for element in flatten(sublist):
File "C:\Users\Mr_Deng\Desktop\test.py", line 6, in flatten
for element in flatten(sublist):
File "C:\Users\Mr_Deng\Desktop\test.py", line 6, in flatten


对字符串进行迭代会出现错误,原因主要是flatten函数迭代时,需要把最终递归结束的迭代器作为原子对象,对于字符串来说并不是这样,在对字符串迭代时,Python并没有将字符串当成原子值,而是作为一个可以展开的序列就像迭代展开,因为就字符串而言,其中没有个字符又是一个可以展开的元素值。其次,对字符串的迭代会形成无穷递归,因为一个字符串的第一个元素是另一个长度为1的字符串,长度为1的字符串也就是字符串本身,又陷入了另一个长度为1的字符串的迭代。

若要对字符串作为基本元素进行迭代,需要在生成器开始的地方添加一个检查语句,将传入的对象和一个字符串拼接,看看是否出现TypeError。

# _*_ coding:utf-8 _*_
__metaclass__=type
def flatten(nested):
try:
try:
nested+''
except TypeError:
pass
else:
raise TypeError

for sublist in nested:
for element in flatten(sublist):
yield(element)
except TypeError:
yield nested

运行结果:
==========RESTART: C:\Users\Mr_Deng\Desktop\test.py==========
>>> list(flatten(['test',['asd',['qwe']],['pou']]))
['test', 'asd', 'qwe', 'pou']
>>>


或者通过instance检查类型:

# _*_ coding:utf-8 _*_
__metaclass__=type
def flatten(nested):
try:
if isinstance(nested,str):
raise TypeError

for sublist in nested:
for element in flatten(sublist):
yield(element)
except TypeError:
yield nested
================= RESTART: C:\Users\Mr_Deng\Desktop\test.py =================
>>> list(flatten(['test',['asd',['qwe']],['pou']]))
['test', 'asd', 'qwe', 'pou']
>>> list(flatten('123'))
['123']
>>> list(flatten([1,2,3,[4,[6,7]]]))
[1, 2, 3, 4, 6, 7]
>>>


参考文章:Magnus Lie Hetland的Python基础教程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息