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

python学习笔记9-迭代器和生成器整理

2012-09-11 14:37 866 查看
一、迭代器
迭代器介绍的还不错的文章:http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html
1、迭代器介绍
Iterator是迭代器的意思,它的作用是一次产生一个数据项,直到没有为止。这样在 for 循环中就可以对它进行循环处理了。
那么它与一般的序列类型(list, tuple等)有什么区别呢?它一次只返回一个数据项,占用更少的内存。但它需要记住当前的状态,以便返回下一数据项。
它是一个有着next()方法的对象。而序列类型则保存了所有的数据项,它们的访问是通过索引进行的。

迭代器和iter()函数
根本上说, 迭代器就是有一个 next()方法的对象, 而不是通过索引来计数. 当你或是一个循环机制(例如 for 语句)需要下一个项时,
调用迭代器的next()方法就可以获得它。条目全部取出后, 会引发一个 StopIteration 异常, 这并不表示错误发生, 只是告诉外部调用者, 迭代完成.

实质上,Python 2.2 序列都是迭代器。Python常见的习惯用法 for elem in lst: 现在实际上让lst产生一个迭代器。然后,for循环反复调用这个迭代器的 .next()方法,
直到它遇到 StopIteration 异常为止。幸运的是,由于所有常见的内置类型自动产生它们的迭代器,所以Python程序员不需要知道这里发生了什么。
实际上,现在字典里有 .iterkeys() 、 .iteritems() 和 .itervalues() 方法来产生迭代器;首要的是在新的习惯用法 for key in dct: 中使用了什么。
同样,通过调用 .readline() 迭代器支持新的习惯用法 for line in file:

迭代器仅是一容器对象,它实现了迭代器协议。它有两个基本方法:
1)next方法
返回容器的下一个元素
2)__iter__方法
返回迭代器自身

1、如何创建迭代器
1)iter()函数创建
对一个对象调用iter()就可以得到它的迭代器. 它的语法如下:
iter(obj)
iter(func, sentinel )
如果你传递一个参数给 iter() , 它会检查你传递的是不是一个序列, 如果是, 那么很简单:
根据索引从 0 一直迭代到序列结束.
>>> i = iter('abc')
>>> i.next()
'a'
>>> i.next()
'b'
>>> i.next()
'c'
>>> i.next()
Traceback (most recent call last):
File "<string>", line 1, in <string>
StopIteration:
2、另一个创建迭代器的方法是使用类
一个实现了 __iter__() 和 next() 方法的类可以作为迭代器使用
class中__init__()方法执行前述的赋值操作。__iter__()仅返回self,这就是如何将一个对象声明为迭代器的方式,
最后,调用next()来得到迭代器中连续的值。next()控制怎么返回下一个值,顺序,倒序,多个步进,随机等。
StopIteration异常需要在next抛出。

#coding=utf-8
class Fib():
def __init__(self, max):
self.max = max
self.a = 0
self.b = 1
def __iter__(self):
return self
def next(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib

for n in Fib(1000):
print n


next倒序的列子

class MyIterator(object):
def __init__(self, step):
self.step = step
def next(self):
"""Returns the next element."""
if self.step==0:
raise StopIteration
self.step-=1
return self.step
def __iter__(self):
"""Returns the iterator itself."""
return self

for i in MyIterator(4):
print i


next随机的例子(永不退出)

from random import choice

class RandSeq(object):
def __init__(self, seq):
self.data = seq

def __iter__(self):
return self

def next(self):
return choice(self.data)
Tl=[1,2,3,4]
T=RandSeq(Tl)
for i in T:print i


这个例子展示了一些我们可以用定制类迭代器来做的与众不同的事情。一个是无穷迭代。因为
我们无损地读取一个序列,所以它是不会越界的。每次用户调用next()时,它会得到下一个迭代值,
但我们的对象永远不会引发StopIteration 异常。

二、生成器
网上这篇文章比较简洁:http://blog.csdn.net/scelong/article/details/6969276
另外一篇很系统的文章:http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html
1、什么是生成器,生成器有什么用?

首先请确信,生成器就是一种迭代器。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。另外,对于生成器的特殊语法支持使得编写一个生成器比自定义一个常规的迭代器要简单不少,所以生成器也是最常用到的特性之一。

当协同程序暂停的时候,我们能从其中获得一个中间的返回值,当调用回到程序中时,能够传入额外或者改变了的参数,但仍能够从
我们上次离开的地方继续,并且所有状态完整。挂起返回出中间值并多次继续的协同程序被称为生成器,那就是pytho的生成器真正在做的事。

生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

在Python2.5 提升让生成器更加接近一个完全的协同程序,因为允许值(和异常)能传回到一个继续的函数。
什么是python式的生成器?从句法上讲,生成器是一个带yield 语句的函数。一个函数或者子程序只返回一次,
但一个生成器能暂停执行并返回一个中间的结果----那就是yield 语句的功能, 返回一个值给调用者并暂停执行。
当生成器的next()方法被调用的时候,它会准确地从离开地方继续(当它返回[一个值以及]控制给调用者时)

2、简单的生成器特性
与迭代器相似,生成器以另外的方式来运作:当到达一个真正的返回或者函数结束没有更多的值返回(当调用next()),一个StopIteration 异常就会抛出。
这里有个例子,简单的生成器:
>>> def simpleGen():
... yield 1
... yield '2---> punch!'
>>> myG=simpleGen()
>>> myG.next()#有点怪怪的,函数对象还有方法,next,send,close
1
>>> myG.next()
'2---> punch!'
>>> myG.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
在接下来的例子中,我们将要创建一个带序列并从那个序列中返回一个随机元素的随机迭代器:
>>> from random import randint
>>> def randGen(aList):
... while len(aList)>0:
... yield aList.pop(randint(0,len(aList)-1))#网上例子这里有错误,随机数的范围有问题,没有len(aList)-1
不同点在于每个返回的元素将从那个队列中消失,像一个list.pop()和random.choice()的结
合的归类。
>>> for item in randGen(['rock', 'paper', 'scissors']):
... print item
...
rock
scissors
paper
使用生成器最好的地方就是当你正迭代穿越一个巨大的数据集合,而重复迭代这个数据集合是
一个很麻烦的事,比如一个巨大的磁盘文件,或者一个复杂的数据库查询。对于每行的数据,你希
望执行非元素的操作以及处理,但当正指向和迭代过它的时候,你“不想失去你的地盘“

2、加强的生成器特性
一些加强特性加入到生成器中,所以除了next()来获得下个生成的值,用户
可以将值回送给生成器[send()],在生成器中抛出异常,以及要求生成器退出[close()]
由于双向的动作涉及到叫做 send()的代码来向生成器发送值(以及生成器返回的值发送回来),
现在yield 语句必须是一个表达式,因为当回到生成器中继续执行的时候,你或许正在接收一个进
入的对象。下面是一个展示了这些特性的,简单的例子。我们用简单的闭包例子,counter:
>>> def counter(start_at=0):
... count = start_at
... while True:
... val=(yield count) #直接返回初始值 同时将值赋给val
... if val is not None: #如yield count返回的不是None 则count=val
... count=val
... else:#是None的情况 count加1,也就是yield coun没有东西返回的时候count加以
... count+=1
...
生成器带有一个初始化的值,对每次对生成器[next()]调用以1 累加计数。用户已可以选择重
置这个值,如果他们非常想要用新的值来调用send()不是调用next()。这个生成器是永远运行的,
所以如果你想要终结它,调用close()方法。如果我们交互的运行这段代码,会得到如下输出:
>>> count=counter(5)
>>> count.next() #有初始值 返回
5
>>> count.next() #现在为NONE count
6
>>> count.send(9)
9
>>> count.next()
10
>>> count.close()
>>> count.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> count=counter()
>>> count.next() #
0

网上精辟总结:生成器和迭代器
生成器是迭代器,同时也并不仅仅是迭代器,不过迭代器之外的用途实在是不多,所以我们可以大声地说:生成器提供了非常方便的自定义迭代器的途径。

三、__getitem__和__iter__以及生成器实现迭代 简单对比
__getitem__和__iter__实现迭代
for循环的作用是从0到更大的索引值,重复对序列进行索引运算,直到检测到超出边界的异常。
__getitem__也可以是Python中一种重载迭代的方式,如果定义了这个方法,for循环每次循环时都会调用类的__getitem__
>>> class stepper:
... def __getitem__(self,i):
... return self.data[i]
...
>>> X=stepper
>>> X=stepper()
>>> X.data='diege'
>>> X[1]
'i'
>>> for item in X:
... print item,
...
d i e g e
任何支持for循环的类也会自动支持Python所有迭代环境,包括成员关系测试in,列表解析,内置函数map,列表和元组赋值运算
以及类型构造方法也会自动调用__getitem__(如果定义的话)
如今,Python中所有的迭代环境都会先尝试__iter__方法,再尝试__getitem__。如果对象不支持迭代协议,就会尝试索引运算。
从技术角度来将,迭代环境是通过调用内置函数iter去尝试寻找__iter__方法来实现的,而这种方法应该返回一个迭代器对象。
如果已经提供了,Python就会重复调用这个迭代器对象的next方法,直到发生StopIteration异常。如果没有找到__iter__方法
,Python会改用__getitem__机制,就像之前那样通过偏移量重复索引,直到引发IndexError异常。
class Squares:
def __init__(self,start,stop):
self.value=start-1
self.stop=stop
def __iter__(self):
return self
def next(self):
if self.value==self.stop:
raise StopIteration
self.value+=1
return self.value**2
>>> from test29 import Squares
>>> for i in Squares(1,5):
... print i,
...
1 4 9 16 25
迭代器对象就是实例self,因为next方法是这个类的一部分。在较为复杂的的场景中,迭代器对象可定义为个别的类或对象,
有自己的状态信息,对相同数据支持多种迭代。以Python raise语句发出信号表示迭代结束。
__iter__对象会在调用过程中明确地保留状态信息。所以比__getitem__具体更好的通用性。
__iter__迭代器比__getitem__更复杂和难用。迭代器是用来迭代,不是随机的索引运算。事实上,迭代器根本没有重载索引表达式
>>> X=Squares(1,5)
>>> X[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Squares instance has no attribute '__getitem__'
__iter__机制是在__getitem__中所见到的其他所有迭代环境的实现方式(成员关系测试,类型构造器,序列赋值运算)。
和__getitem__不同的是,__iter__只循环一次,而不是循环多次,循环之后就变为空,每次新的循环,都得创建一个新的。
>>> X=Squares(1,5)
>>> [n for n in X]
[1, 4, 9, 16, 25]
>>> [n for n in X]
[]
如果使用生成器函数编写,这个例子可能更简单
>>> from __future__ import generators
>>> def gsquares(start,stop):
... for i in range(start,stop+1):
... yield i**2
...
>>> for i in gsquares(1,5):
... print i,
...
1 4 9 16 25
和类不同的是,这个函数会自动在迭代中存储存其状态。这是假设的例子,实际上,可以跳过这两种技术,只用for循环,map或列表解析
一次创建这个列表。
>>> [x**2 for x in range(1,6)]
[1, 4, 9, 16, 25
有多个迭代器的对象。

__author__ = 'jin'

from random import randint
class RandomOmg():
def __init__(self, n):
self.number =n
r = randint(1,n-1)
L = [0 for i in xrange(n-1)]
L.insert(r,1)
self.L = L
def __iter__(self):
return self

def next(self):
return  self.L.pop(0)

class RandomEndOmg(RandomOmg):
#最后一个才出现1
def __init__(self, n):
self.number =n
r = randint(1,n-1)
L = [0 for i in xrange(n-1)]
L.append(1)
self.L = L

def test(n):
r = randint(1,n-1)
L = [0 for i in xrange(4)]
L.insert(r,1)
while L:
yield L.pop(0)

if __name__ == '__main__':
A=test(5)
print A.next()
print A.next()
print A.next()
print A.next()
print A.next()
#print A.next()
print '#'*80
C=RandomOmg(5)
print C.next()
print C.next()
print C.next()
print C.next()
print C.next()
#print A.next()
print '#'*80
C=RandomEndOmg(5)
print C.next()
print C.next()
print C.next()
print C.next()
print C.next()


永不退出设置

__author__ = 'kaijin'

from random import randint
class RandomOmg():
def __init__(self, n):
self.number = n
r = randint(1,n-1)
L = [0 for i in xrange(n-1)]
L.insert(r,1)
self.L = L

def __iter__(self):#貌似这个函数用不到
return self

def next(self):
if self.L:
return  self.L.pop(0)
else:
self.__init__(self.number)
return  self.L.pop(0)

class RandomEndOmg(RandomOmg):
#最后一个才出现1
def __init__(self, n):
self.number = n
r = randint(1,n-1)
L = [0 for i in xrange(n-1)]
L.append(1)
self.L = L

if __name__ == '__main__':
a = RandomOmg(5)
print [a.next() for i in xrange(20)]
b = RandomEndOmg(5)
print [b.next() for i in xrange(20)]


改进

# coding=utf8
__author__ = 'kaijin'

from random import randint

class RandomOmg():
def __init__(self, n):
self._number = n
self._Generate()

def _Generate(self):
r = randint(0, self._number-1)
L = [0 for i in xrange(self._number)]
L[r] = 1
self.L = L

def next(self):
if self.L:
return self.L.pop(0)
else:
self._Generate()
return self.L.pop(0)

class RandomEndOmg(RandomOmg):
#最后一个才出现1
def _Generate(self):
L = [0 for i in xrange(self._number)]
L[-1] = 1
self.L = L

if __name__ == '__main__':
a = RandomOmg(5)
print [a.next() for i in xrange(20)]
b = RandomEndOmg(5)
print [b.next() for i in xrange(20)]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: