Python中的可迭代对象、迭代器、For循环工做机制、生成器

1.iterable iterator区别python

要了解二者区别,先要了解一下迭代器协议:网络

迭代器协议是指:对象须要提供__next__()方法,它返回迭代中的元素,在没有更多元素后,抛出StopIteration异常,终止迭代。函数

可迭代对象就是:实现了迭代器协议的对象。工具

协议是一种约定,可迭代对象实现迭代器协议,Python的内置工具(如for循环,sum,min,max函数等)经过迭代器协议访问对象,所以,for循环并不须要知道对象具体是什么,只须要知道对象可以实现迭代器协议便可。学习

迭代器(iterator)与可迭代对象(iterable)并非同一个概念。spa

直观上:code

1.可迭代对象(iterable):凡是具备__iter__的方法的类,都是可迭代的类。可迭代类建立的对象实现了__iter__方法,所以就是可迭代对象。用list、tuple等容器建立的对象,都是可迭代对象。可迭代对象经过__iter__方法返回一个迭代器,而后在内部调用__next__方法进行迭代,最后没有元素时,抛出异常(这个异常python本身会处理,不会让开发者看见)。对象

2.迭代器(iterator):迭代器对象必须同时实现__iter__和__next__方法才是迭代器。对于迭代器来讲,__iter__ 返回的是它自身 self,__next__ 则是返回迭代器中的下一个值,最后没有元素时,抛出异常(异常能够被开发者看到)。教程

从上面2点能够看出:图片

1.迭代器必定是可迭代对象,由于它实现了__iter__()方法;

2.经过iter()方法(在类的内部就是__iter__)可以使一个可迭代对象返回一个迭代器。

Python学习qun:5563,70268,这里是python学习者汇集地,欢迎喜欢Python的小伙伴!

3.迭代器的 iter 方法返回的是自身,并不产生新的迭代器对象。而可迭代对象的 iter 方法一般会返回一个新的迭代器对象。

第3点性质正是可迭代对象能够重复遍历的缘由(每次返回一个独立的迭代器,就能够保证不一样的迭代过程不会互相影响);而迭代器因为返回自身,所以只能遍历一次。
上面3点能够经过下面的例子看出来:

from collections import Iterable
from collections import Iterator
print isinstance(iter([1,2]),Iterator)
print isinstance(iter([1,2]),Iterable)
print isinstance([1,2],Iterator)
print isinstance([1,2],Iterable)
##result
True
True
False
True
##id能够查看一个对象在内存中的地址
test=[1,2,3]
testIter=iter(test)
print id(testIter)
print id(testIter)
print id(iter(test))
print id(iter(test))
print id(test.__iter__())
print id(test.__iter__())
##result:可迭代对象每次调用iter方法都会返回一个新的迭代器对象,而迭代器对象调用iter方法返回自身
67162576 
67162576 
67162688 
67162632 
67162856 
67163024
2.iterable的工做机制

拿一个例子看看,首先定义一个有__iter__方法,可是没有next()方法的类 (PS:在python2中是next(),python3是__next__()):

from collections import Iterable, Iterator
class Student(object):
 def __init__(self,score):
 self.score=score
 def __iter__(self):
 return iter(self.score)
 
test= Student([80,90,95])
print isinstance(test, Iterable)
print isinstance(test, Iterator)
for i in test:
 print i
##result
True
False
80
90
95
##可重复遍历
for i in test:
 print i
##result
80
90
95

上面代码的结果印证了定义中提到的:

缺乏了next()方法,可迭代对象就不是迭代器。

此外,注意到:可迭代对象经过__iter__方法每次都返回了一个独立的迭代器,这样就能够保证不一样的迭代过程不会互相影响。

也就是说,经过iterable能够实现重复遍历,而迭代器是没法重复遍历的!

所以,若是想要把可迭代对象转变为迭代器,能够先调用iter()方法返回一个迭代器。而后就能够用next()不断迭代了!

print isinstance(iter(test),Iterator)
testIter=iter(test)
print testIter.next()
print testIter.next()
print testIter.next()
##result
True
80
90
95

一旦取完了可迭代对象中全部的元素,再次调用next就会发生异常

print testIter.next()
##result
StopIteration:

3.迭代器Iterator的工做机制

看下面这个例子:

class Student(object):
 def __init__(self,score):
 self.score=score
 def __iter__(self):
 return self
 
 def next(self):
 if self.score<100:
 self.score+=1
 return self.score
 else:
 raise StopIteration()
 
test= Student(90)
print isinstance(test, Iterable)
print isinstance(test, Iterator)
print test.next()
print test.next()
print test.next()
for i in test:
 print i
##result
True
True
91
92
93
94
95
96
97
98
99
100
##若是此时再对test这个迭代器调用next方法,就会抛出异常
test.next()
##result
StopIteration:

这个例子印证了定义中的:迭代器对象必须同时实现__iter__和__next__方法才是迭代器。

那么,使用迭代器好处在哪呢?

Python的Iterator对象表示的是一个数据流,Iterator对象能够被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。能够把这个数据流看作是一个有序序列,但咱们却不能提早知道序列的长度,只能不断经过next()函数实现按需计算下一个数据,因此Iterator的计算是惰性的,只有在须要返回下一个数据时它才会计算。

一个很常见的应用就是:Python在处理列表的时候,是直接把整个列表读进内存的,当遇到大量样本时的时候会变得很慢。而迭代器的优点在于只把须要的元素读进内存,所以占用内存更少。

换句话说,迭代器是一种惰性求值模式,它是有状态的,只有在调用时才返回值,没有调用的时候就等待下一次调用。这样就节省了大量内存空间。

4.for循环的工做机制

有了上面2个例子,就能够总结一下在可迭代对象与迭代器中的For循环工做机制了。

当对象自己就是迭代器时,For循环工做机制:

1.调用 __iter__方法,返回自身self,也就是返回迭代器。

2.不断地调用迭代器的next()方法,每次按序返回迭代器中的一个值。

3.迭代到最后没有元素时,就抛出异常 StopIteration。

在可迭代对象中,for循环工做机制:

1.先判断对象是否为可迭代对象(等价于判断有没有__iter__或__getitem__方法),没有的话直接报错,抛出TypeError异常。有的话,调用 __iter__方法,返回一个迭代器。

2.在python内部不断地调用迭代器的__next__方法,每次按序返回迭代器中的一个值。

3.迭代到最后没有元素时,就抛出异常 StopIteration,这个异常 python 本身会处理,不会暴露给开发者。

借用网络上的一张图直观理解一下:

图片描述

Python中的可迭代对象、迭代器、For循环工做机制、生成器
此外,还要注意,python中的for循环其实兼容了两种机制:

1.若是对象有__iter__会返回一个迭代器。

2.若是对象没有__iter__,可是实现了__getitem__,会改用下标迭代的方式。

__getitem__能够帮助一个对象进行取数和切片操做。

当for发现没有__iter__可是有__getitem__的时候,会从0开始依次读取相应的下标,直到发生IndexError为止,这是一种旧的迭代协议。iter方法也会处理这种状况,在不存在__iter__的时候,返回一个下标迭代的iterator对象来代替。一个重要的例子是str,字符串就是没有__iter__方法的,可是却依然能够迭代,缘由就是其在for循环时调用了__getitem__方法。

看一个例子:

from collections import Iterable, Iterator
    class Student(object):
     def __init__(self,score):
     self.score=score
     def __getitem__(self,n):
     return self.score[n]
     
    test= Student([80,90,95])
    print isinstance(test, Iterable)
    print isinstance(test, Iterator)
    print isinstance(iter(test), Iterable)
    print isinstance(iter(test), Iterator)
    for i in test:
     print i
    ##result
    False
    False
    True
    True
    80
    90
    95
for i in range(0,3):
     print test[i]
    ##result
    80
    90
    95
for i in iter(test):
     print i
    ##result
    80
    90
    95

能够看到,实现了__getitem__方法的对象自己,尽管不是iterable与iterator,仍旧是能够调用for循环的。

经过iter方法,返回一个下标迭代的iterator对象。

5.generator的原理

最后说一下生成器,生成器是一种特殊的迭代器,固然也是可迭代对象。

对于生成器,Python会自动实现迭代器协议,以便应用到迭代中(如for循环,sum函数)。因为生成器自动实现了迭代器协议,因此,咱们能够调用它的next方法,而且,在没有值能够返回的时候,生成器自动产生StopIteration异常。

建立生成器的方法:将return 改成yield。具体的实现网络上教程不少,不细说了。

Python学习qun:5563,70268,这里是python学习者汇集地,欢迎喜欢Python的小伙伴!

6.总结

到一幅图片很好的描述了本文的全部内容,就拿它做为文末的总结吧!

图片描述

相关文章
相关标签/搜索