当解释器想要迭代访问一个对象的时候,会调用iter(x)
。
-
iter
方法会检查对象是否实现了__iter__
方法,如果实现了,就调用它返回一个迭代器。 - 如果没有实现
__iter__
,则去尝试调用__getitem__
方法,解释器创建一个迭代器,按下表从0开始依次获取元素。 - 如果上面两个方法都没有实现,则抛出
TypeError
。错误信息为:"XXX object is not iterable"。
需要说明的一点是,在Python 2.X的环境下,常常有range()
和xrange()
两种方式来创建一个可迭代对象(在Python 3.X中统一使用range()
,效果和Python 2.X中的xrange()
一致)。两者的区别在于前者返回的是一个计算好的列表,而后者返回的是一个惰性可迭代对象。列表并不是迭代器。
>>> a = range(7)
>>> b = xrange(7)
>>> c = (x for x in range(7))
>>> next(a)
TypeError: list object is not an iterator.
>>> next(b)
TypeError: xrange object is not an iterator.
>>> next(c)
0
那么,迭代器和可迭代对象又有什么区别呢?range
和xrange
对象都可以供多次迭代访问,而迭代器(上文中的c对象)仅可供一次迭代访问:从下表0开始到StopIteration
。
使用 iter 内置函数可以从可迭代对象获取迭代器的对象。如果对象实现了能返回迭代器的 __iter__
方法,那么对象就是可迭代的。序列都可以迭代;实现了 __getitem__
方法,而且其参数是从零开始的索引,这种对象也可以迭代。
最后用一个自定义的等差数列来说明可迭代对象的实现:
class ArithmeticProgression:
def __init__(self, begin, step, end=None):
self.begin = begin
self.step = step
self.end = end # None -> 无穷数列
def __iter__(self):
result = type(self.begin + self.step)(self.begin)
forever = self.end is None
index = 0
while forever or result < self.end:
yield result
index += 1
result = self.begin + self.step * index
# 也可以用下面方法来实现
def aritprog_gen(begin, step, end=None):
result = type(begin + step)(begin)
forever = end is None
index = 0
while forever or result < end:
yield result
index += 1
result = begin + step * index
# 或者借助itertools来实现
def aritprog_gen(begin, step, end=None):
import itertools
first = type(begin + step)(begin)
ap_gen = itertools.count(first, step)
if end is not None:
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
return ap_gen