可迭代对象 实现了__iter__魔术方法的对象是可迭代对象(Iterable)
迭代器 实现了__iter__和__next__魔术方法的对象是迭代器(Iterator)
迭代器和可迭代对象的区别在于迭代器是可以通过for循环来遍历的,而可迭代对象如果没有实现__next__魔术方法是不能用for来遍历的。
判断一个对象是否是可迭代对象 使用isinstance()判断一个对象是否是Iterable
from collections import Iterable, Iterator
ret = isinstance([1, 2, 3], Iterable)
print(ret)
ret = isinstance((1, 2, 3), Iterable)
print(ret)
ret = isinstance("abc", Iterable)
print(ret)
ret = isinstance(123, Iterable)
print(ret)
输出:
True
True
True
False
创建一个迭代器 要求:实现__iter__()魔术方法并返回一个特殊的可迭代对象,这个可迭代对象需要实现 next() 方法并通过 StopIteration 异常标识迭代的完成。 咋一看可能不太懂什么意思,下面通过几个案例讲解下。
实现了__iter__()魔术方法的对象是一个可迭代对象,但是没法通过for循环进行遍历。
from collections import Iterable, Iterator
class MyRangeIterator(object):
def __init__(self, start, end):
self.index = start
self.end = end
def __iter__(self):
return self
myrange = MyRangeIterator(1, 2)
print(isinstance(myrange, Iterable))
for x in myrange:
print(x)
输出:
True
Traceback (most recent call last):
File "D:/pycharm workspace/oopdemo/iterator_generator/IteratorDemo03.py", line 22, in <module>
for x in myrange:
TypeError: iter() returned non-iterator of type 'MyRangeIterator'
这个MyRangeIterator对象实现了__iter__魔术方法,返回的是它自身实例,但是MyRangeIterator并没有实现__next__()魔术方法,所以没法使用for遍历,下面实现__next__魔术方法后就可以使用for遍历了。
class MyRangeIterator(object):
def __init__(self, start, end):
self.index = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.index < self.end:
temp = self.index
self.index += 1
return temp
else:
raise StopIteration()
myrange = MyRangeIterator(1, 3)
print(isinstance(myrange, Iterable))
for x in myrange:
print(x)
输出:
True
1
2
注意:在实现__next__魔术方法的时候,当遍历完成后需要抛出StopIteration异常,否则迭代过程不会停下来。 __next__魔术方法对应的是next()函数,所以上面的迭代器对象还可以这样使用。
print(next(myrange))
在使用for循环遍历的时候,内部其实是自动通过next函数来取值。
上面那种__iter__方法直接返回自身实例来实现迭代器的方式实际上有个坑,还是上面那个案例,如果使用两个for循环来遍历,第二个for循环遍历不到值。
myrange = MyRangeIterator(1, 3)
print(isinstance(myrange, Iterable))
for x in myrange:
print(x)
for y in myrange:
print(y)
输出:
True
1
2
第二个for循环没有值输出,原因是myrange这个实例的index值发生了变化,所以再使用for遍历的时候,index已经达到最大值了,不会再取到值。所以实现迭代器最好的方法是每次都返回一个迭代器实例,像下面这样。
class MyRangeIterator(object):
def __init__(self, start, end):
self.index = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.index < self.end:
temp = self.index
self.index += 1
return temp
else:
raise StopIteration()
class MyRange(object):
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return MyRangeIterator(self.start, self.end)
ret = MyRange(1, 3)
for x in ret:
print(x)
print("="*10)
for y in ret:
print(y)
输出:
1
2
==========
1
2
这个例子中MyRange没有实现__next__方法,而是在__iter__魔术方法中返回了一个实现了__next__的可迭代对象。所以它也是一个迭代器对象,而且每次使用for遍历的时候都会通过__iter__返回一个新的MyRangeIterator实例。
其实文章开头对迭代器的定义有点不准确,实际还可以通过生成器的方式来得到迭代器,下一节会介绍生成器!
本人是做大数据开发的,在微信上开了个个人号,会经常在上面分享一些学习心得,原创文章都会首发到公众号上,感兴趣的盆友可以关注下哦!