python中的迭代器和生成器(一)——迭代器

在学习python的资料中,经常看到迭代器和生成器,这样的名词。但到底什么是迭代器,什么是生成器?为什么会出现这样的类型,他们有什么便捷之处?

什么是迭代?

    简单的讲就是循环操作,重复某一个过程很多次。可以用for循环对可迭代对象进行迭代。

什么是可迭代对象?

    常见的迭代对象有序列,字典和文件等,另外还有其他的对象。这些可迭代对象的特点是:实现了__iter__方法。例如:

>>> alist=[1,2,3]
>>> lst=alist.__iter__()
>>> type(lst)
<type 'listiterator'>
>>> atuple=(1,2,3)
>>> tup=atuple.__iter__()
>>> type(tup)
<type 'tupleiterator'>
>>> afile=open(r"e:\test.txt",'r')
>>> fl=afile.__iter__()
>>> type(fl)
<type 'file'>
>>> astr='abc'
>>> string=astr.__iter__()

Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    string=astr.__iter__()
AttributeError: 'str' object has no attribute '__iter__'
>>> iter(astr)
<iterator object at 0x0212BE70>
>>> string=iter(astr)
>>> anum=1
>>> iter(anum)

Traceback (most recent call last):
  File "<pyshell#66>", line 1, in <module>
    iter(anum)
TypeError: 'int' object is not iterable
>>> type(string)
<type 'iterator'>
>>> 

我们注意到,__iter__方法返回的对象就是迭代器对象。对于字符串astr来说,没有__iter__方法,但是可以用内建函数iter()来调用,所以是可迭代对象。而对于数字anum来说,iter()无法调用,所以是不可迭代的对象。因此可以用iter()来判断一个对象是否是可迭代对象。

内建函数iter()和方法__iter__的关系:

>>> help(list.__iter__)
Help on wrapper_descriptor:

__iter__(...)
    x.__iter__() <==> iter(x)

>>> 

什么是迭代器?

迭代器就是有一个 next() 方法的对象。

在Python3.0中迭代器规则有些变化,迭代器对象应该实现__next__方法,而不是next。而新的内建函数next可以用于访问这个方法。换句话说,next(it)等同于3.0之前版本中的it.next()。

看看上面的迭代器是否都有next():

>>> lst.next()
1
>>> tup.next()
1
>>> fl.next()
'\xef\xbb\xbfHello world!\n'
>>> string.next()
'a'

如何迭代?

在后台,for 语句在容器对象中调用iter().该函数返回一个定义了next()方法的迭代器对象,它在容器中逐一访问元素。没有后续元素时,next()就会抛出一个 StopIteration 异常, 这并不表示错误发生, 只是通知外部调用者,for语句循环结束, 迭代完成。

以下是其工作原理的示例:

>>> s='abc'
>>> it=iter(s)
>>> it
<iterator object at 0x0228F9D0>
>>> it.next()
'a'
>>> it.next()
'b'
>>> it.next()
'c'
>>> it.next()

Traceback (most recent call last):
  File "<pyshell#33>", line 1, in <module>
    it.next()
StopIteration
>>> 

如何自定义迭代器?

    了解了迭代器协议的后台机制,就可以很容易的给自己的类添加迭代器行为。定义一个  __iter__() 方法,使其返回一个带有  next() 方法的对象。如果这个类已经定义了  next(),那么 __iter__() 只需要返回self():

>>> class Reversestr:
	'Iterator for looping over a swquence backwards'
	def __init__(self,data):
		self.data = data
		self.index = len(data)
	def __iter__(self):
		return self
	def next(self):
		if self.index == 0:
			raise StopIteration
		self.index = self.index - 1
		return self.data[self.index]

	
>>> for char in Reversestr('spam'):
	print char

	
m
a
p
s
>>> 

迭代器的优点是什么?列表不可以吗?

    如果有一个可迭代计算值的函数,那么在使用时可能只需要获得指定的值,而不需要获得所有值的列表。这时候,如果数值很多,使用列表会占用太多的内存。当然,使用迭代器更简单更优雅。

看一个不使用列表的例子,因为那样列表的长度必须无限。

>>> 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 = Fibs()
在for循环中使用该对象——比如查找在斐波那契数列中比500大的最小的数:

>>> for f in fibs:
	if f > 500:
		print f
		break

	
610
因为设置了break,所以循环停止了,否则会一直继续下去。

迭代器和可迭代对象之间的关系

内建函数iter可以从可迭代对象中获取迭代器,以list为例:

>>> a=[1,2,3]
>>> type(a)
<type 'list'>
>>> it=iter(a)
>>> type(it)
<type 'listiterator'>
>>> it.next()
1
>>> it.next()
2
>>> it.next()
3
>>> it.next()

Traceback (most recent call last):
  File "<pyshell#140>", line 1, in <module>
    it.next()
StopIteration
>>> 

注:iter(a)和a.__iter__()等价。

反过来,使用list构造方法可以将迭代器转化为列表:

>>> a=[1,2,3]
>>> it=iter(a)
>>> list(it)
[1, 2, 3]
>>> 

总结

可迭代对象:一个实现了__iter__方法的对象

迭代器:一个实现了next方法的对象。

关系:__iter__方法返回一个迭代器。


参考资料:

  1. Python参考手册
  2. Python基础教程(第2版)

你可能感兴趣的:(迭代器,迭代,可迭代对象)