python将list转换为迭代器代码_(转)python基础学习-----⽣
成器和迭代器
在Python中,很多对象都是可以通过for语句来直接遍历的,例如list、string、dict等等,这些对象都可以被称为可迭代对象。⾄于说哪些对象是可以被迭代访问的,就要了解⼀下迭代器相关的知识了。
迭代器
迭代器对象要求⽀持迭代器协议的对象,在Python中,⽀持迭代器协议就是实现对象的__iter__()和next()⽅法。其中__iter__()⽅法返回迭代器对象本⾝;next()⽅法返回容器的下⼀个元素,在结尾时引发StopIteration异常。
__iter__()和next()⽅法
这两个⽅法是迭代器最基本的⽅法,⼀个⽤来获得迭代器对象,⼀个⽤来获取容器中的下⼀个元素。
对于可迭代对象,可以使⽤内建函数iter()来获取它的迭代器对象:
例⼦中,通过iter()⽅法获得了list的迭代器对象,然后就可以通过next()⽅法来访问list中的元素了。当容器中没有可访问的元素后,next()⽅法将会抛出⼀个StopIteration异常终⽌迭代器。
其实,当我们使⽤for语句的时候,for语句就会⾃动的通过__iter__()⽅法来获得迭代器对象,并且通过next()⽅法来获取下⼀个元素。
⾃定义迭代器
了解了迭代器协议之后,就可以⾃定义迭代器了。
下⾯例⼦中实现了⼀个MyRange的类型,这个类型中实现了__iter__()⽅法,通过这个⽅法返回对象本⾝作为迭代器对象;同时,实现了next()⽅法⽤来获取容器中的下⼀个元素,当没有可访问元素后,就抛出StopIteration异常。
class MyRange(object):
def __init__(self, n):
self.idx = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.idx < self.n:
val = self.idx
self.idx += 1
return val
else:
raise StopIteration()
这个⾃定义类型跟内建函数xrange很类似,看⼀下运⾏结果:
myRange = MyRange(3)
for i in myRange:
print i
迭代器和可迭代对象
在上⾯的例⼦中,myRange这个对象就是⼀个可迭代对象,同时它本⾝也是⼀个迭代器对象。
看下⾯的代码,对于⼀个可迭代对象,如果它本⾝⼜是⼀个迭代器对象,就会有下⾯的 问题,就没有办法⽀持多次迭代。
为了解决上⾯的问题,可以分别定义可迭代类型对象和迭代器类型对象;然后可迭代类型对象的__iter__()⽅法可以获得⼀个迭代器类型的对象。看下⾯的实现:
class Zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
return ZrangeIterator(self.n)
class ZrangeIterator:
def __init__(self, n):
self.i = 0
python代码转换self.n = n
def __iter__(self):
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
zrange = Zrange(3)
print zrange is iter(zrange)
print [i for i in zrange]
print [i for i in zrange]
代码的运⾏结果为:
其实,通过下⾯代码可以看出,list类型也是按照上⾯的⽅式,list本⾝是⼀个可迭代对象,通过iter()⽅法可以获得list的迭代器对象:
⽣成器
在Python中,使⽤⽣成器可以很⽅便的⽀持迭代器协议。⽣成器通过⽣成器函数产⽣,⽣成器函数可以通过常规的def语句来定义,但是不⽤return返回,⽽是⽤yield⼀次返回⼀个结果,在每个结果之间挂起和继续它们的状态,来⾃动实现迭代协议。
也就是说,yield是⼀个语法糖,内部实现⽀持了迭代器协议,同时yield内部是⼀个状态机,维护着挂
起和继续的状态。
下⾯看看⽣成器的使⽤:
在这个例⼦中,定义了⼀个⽣成器函数,函数返回⼀个⽣成器对象,然后就可以通过for语句进⾏迭代访问了。
其实,⽣成器函数返回⽣成器的迭代器。 "⽣成器的迭代器"这个术语通常被称作"⽣成器"。要注意的是⽣成器就是⼀类特殊的迭代器。作为⼀个迭代器,⽣成器必须要定义⼀些⽅法,其中⼀个就是next()。如同迭代器⼀样,我们可以使⽤next()函数来获取下⼀个值。
⽣成器执⾏流程
下⾯就仔细看看⽣成器是怎么⼯作的。
从上⾯的例⼦也可以看到,⽣成器函数跟普通的函数是有很⼤差别的。
结合上⾯的例⼦我们加⼊⼀些打印信息,进⼀步看看⽣成器的执⾏流程:
通过结果可以看到:
当调⽤⽣成器函数的时候,函数只是返回了⼀个⽣成器对象,并没有 执⾏。
当next()⽅法第⼀次被调⽤的时候,⽣成器函数才开始执⾏,执⾏到yield语句处停⽌
next()⽅法的返回值就是yield语句处的参数(yielded value)
当继续调⽤next()⽅法的时候,函数将接着上⼀次停⽌的yield语句处继续执⾏,并到下⼀个yield处停⽌;如果后⾯没有yield就抛出StopIteration异常
⽣成器表达式
在开始介绍⽣成器表达式之前,先看看我们⽐较熟悉的列表解析( List comprehensions),列表解析⼀般都是下⾯的形式。
[expr for iter_var in iterable if cond_expr]
迭代iterable⾥所有内容,每⼀次迭代后,把iterable⾥满⾜cond_expr条件的内容放到iter_var中,再在表达式expr中应该iter_var的内容,最后⽤表达式的计算值⽣成⼀个列表。
例如,⽣成⼀个list来保护50以内的所以奇数:
[i for i in range(50) if i%2]
⽣成器表达式是在python2.4中引⼊的,当序列过长, ⽽每次只需要获取⼀个元素时,应当考虑使⽤⽣成器表达式⽽不是列表解析。⽣成器表达式的语法和列表解析⼀样,只不过⽣成器表达式是被()括起来的,⽽不是[],如下:
(expr for iter_var in iterable if cond_expr)
看⼀个例⼦:
⽣成器表达式并不是创建⼀个列表, ⽽是返回⼀个⽣成器,这个⽣成器在每次计算出⼀个条⽬后,把这个条⽬"产⽣"(yield)出来。 ⽣成器表达式使⽤了"惰性计算"(lazy evaluation),只有在检索时才被赋值(evaluated),所以在列表⽐较长的情况下使⽤内存上更有效。
继续看⼀个例⼦:
从这个例⼦中可以看到,⽣成器表达式产⽣的⽣成器,它⾃⾝是⼀个可迭代对象,同时也是迭代器本⾝。
递归⽣成器
⽣成器可以向函数⼀样进⾏递归使⽤的,下⾯看⼀个简单的例⼦,对⼀个序列进⾏全排列:
def permutations(li):
if len(li) == 0:
yield li
else:
for i in range(len(li)):
li[0], li[i] = li[i], li[0]
for item in permutations(li[1:]):
yield [li[0]] + item
for item in permutations(range(3)):
print item
代码的结果为:
⽣成器的send()和close()⽅法
⽣成器中还有两个很重要的⽅法:send()和close()。
send(value):
从前⾯了解到,next()⽅法可以恢复⽣成器状态并继续执⾏,其实send()是除next()外另⼀个恢复⽣成器的⽅法。
Python 2.5中,yield语句变成了yield表达式,也就是说yield可以有⼀个值,⽽这个值就是send()⽅法的参数,所以send(None)和next()是等效的。同样,next()和send()的返回值都是yield语句处的参数(yielded value)
关于send()⽅法需要注意的是:调⽤send传⼊⾮None值前,⽣成器必须处于挂起状态,否则将抛出异常。也就是说,第⼀次调⽤时,要使⽤next()语句或send(None),因为没有yield语句来接收这个值。
close():
这个⽅法⽤于关闭⽣成器,对关闭的⽣成器后再次调⽤next或send将抛出StopIteration异常。
下⾯看看这两个⽅法的使⽤:
总结
本⽂介绍了Python迭代器和⽣成器的相关内容。
通过实现迭代器协议对应的__iter__()和next()⽅法,可以⾃定义迭代器类型。对于可迭代对象,for语
句可以通过iter()⽅法获取迭代器,并且通过next()⽅法获得容器的下⼀个元素。
像列表这种序列类型的对象,可迭代对象和迭代器对象是相互独⽴存在的,在迭代的过程中各个迭代器相互独⽴;但是,有的可迭代对象本⾝⼜是迭代器对象,那么迭代器就没法独⽴使⽤。
itertools模块提供了⼀系列迭代器,能够帮助⽤户轻松地使⽤排列、组合、笛卡尔积或其他组合结构。
⽣成器是⼀种特殊的迭代器,内部⽀持了⽣成器协议,不需要明确定义__iter__()和next()⽅法。
⽣成器通过⽣成器函数产⽣,⽣成器函数可以通过常规的def语句来定义,但是不⽤return返回,⽽是⽤yield⼀次返回⼀个结果。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论