详解python可迭代对象、迭代器和生成器

可迭代对象

   什么是可迭代对象?顾名思义就是可以迭代的一个对象,再通俗点就是可以被for循环遍历的对象,如常用的list、str等数据类型。我们可以使用isinstance来判断这个数据是否是可迭代对象,在此要先从Iterable包中导入模块collections。

from collections import Iterable
a = 1 #int
b= [1,2,3] #list
c= "abc" #str
d = {1,2,3} #set
e = (1,2,3) #tuple
f = {1:'a',2:'b'}#dict
g = open('test.py')
print (isinstance(a,Iterable))
print (isinstance(b,Iterable))
print (isinstance(c,Iterable))
print (isinstance(d,Iterable))
print (isinstance(e,Iterable))
print (isinstance(f,Iterable))
print (isinstance(g,Iterable))
False
True
True
True
True
True
True

可以看出除了像int、float、complex这种基本类型外,其他的数据类型都是属于可迭代对象,包括文件对象。他们之所以被python内部认为是一种可迭代对象,是因为他们都具有__iter__方法,用hasattr可以看出来。

a = 1 #int
b= [1,2,3] #list
c= "abc" #str
d = {1,2,3} #set
e = (1,2,3) #tuple
f = {1:'a',2:'b'}#dict
g = open('test.py')
print (hasattr(a,'__iter__'))
print (hasattr(b,'__iter__'))
print (hasattr(c,'__iter__'))
print (hasattr(d,'__iter__'))
print (hasattr(e,'__iter__'))
print (hasattr(f,'__iter__'))
print (hasattr(g,'__iter__'))
False
True
True
True
True
True
True

靠__iter__这个方法我们就可以自己创造一个可迭代对象,如下所示:

from collections import Iterable
class A():
    def __init__(self):
        pass
    def __iter__(self):
        pass
class B():
    def __init__(self):
        pass
a = A()
b = B()
print (isinstance(a,Iterable))
print (isinstance(b,Iterable))
True
False

上面代码可以看出来,在一个类中加入了__iter__这个方法,实例化的对象就会被python内部认为是一个可迭代的对象。

迭代器

   上面介绍了什么是可迭代对象。那么什么是迭代器呢?迭代器跟可迭代对象又是什么关系呢?那么先解释第一问,迭代器对象是要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法。其中__iter__()方法返回迭代器对象本身;next()方法返回容器的下一个元素,在结尾时引发StopIteration异常。事实上,我们可以用上述的方法来检测一下那些数据类型是不是属于迭代器。

from collections import Iterator
a = 1 #int
b= [1,2,3] #list
c= "abc" #str
d = {1,2,3} #set
e = (1,2,3) #tuple
f = {1:'a',2:'b'}#dict
g = open('test.py')
print (isinstance(a,Iterator))
print (isinstance(b,Iterator))
print (isinstance(c,Iterator))
print (isinstance(d,Iterator))
print (isinstance(e,Iterator))
print (isinstance(f,Iterator))
print (isinstance(g,Iterator))
False
False
False
False
False
False
True

结果显示除了文件对象,其他的数据类型都不是迭代器,上面说过,迭代器必须同时有__iter__和__next__方法,我们可以看看文件对象和列表,比较他们是否有__next__方法,依次验证。

print (hasattr(b,'__next__'))#list
print (hasattr(g,'__next__'))#文件对象
False
True

再讲一下可迭代对象和迭代器的区别,我们都知道在学习python时流行这么一句话“一切皆对象”,那么显然易见可迭代对象就包括了迭代器,之所以把把文件对象又叫做迭代器,是因为它多了一种__next__方法而已。

知道了上述所讲的内容后,我们就可以试着自己写一个迭代器,无非就是加入__iter__和__next__方法。

from collections import Iterator

class A():
    def __init__(self,i):
        self.n = 0
        self.i = i
    def __iter__(self):
        return self
    def __next__(self):
        if self.i > self.n:
            var = self.i
            self.i -= 1
            return var
        else:
            raise StopIteration()
a = A(5)
print (isinstance(a,Iterator))
for i in a:
    print (i)


#True
#5
#4
#3
#2
#1

创建一个迭代器还可以使用python内置的函数iter和next,如下:

from collections import Iterator

a = [1,2,3]
b = iter(a)
print (next(b))
print (isinstance(b,Iterator))
1
True

上述代码用iter将一个可迭代对象变成了一个迭代器,然后就可以使用迭代器的专用方法next来一个一个地取出数据。

for循环内部机制

a = [1,2,3]
for i in a:
    print(i)

#1
#2
#3

  为什么一个列表放在for循环里就可以很快地取出内部所有元素呢?正常来说,一个可迭代对象是不能直接从其中取出元素的,但放在for循环里就可以,这是因为在循环时,它会先自动调用__iter__方法将列表变成一个迭代器,然后这个迭代器再调用其__next__()方法。

生成器

   首先我们需要知道的是生成器本质就是一个迭代器,它同样拥有__iter__和__next__两个方法。与迭代器不同的是,他可以产生延迟操作,既不会立即出现结果,而是在你需要的时候才产生。生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。

def A(n):
    print ("----生成器开始-----")
    i = 1
    while n != 0:
        print("开始循环第%d次"%i)
        yield n
        n -= 1
        print ("第%d循环结束"%i)
        i += 1
    print ("----生成器结束----")

a = A(3)
print (a.__next__())
----生成器开始-----
开始循环第1次
5

可以看出,生成器函数直接调用并不会马上执行,只有用next方法才能开始执行,next一次,他会直到碰到yield就会马上结束,当再次写一个next方法时(如下所示),它会在yield后继续执行下去,直到碰到下一个yield才会停止。如果没有找到yield,就会报出异常。

----生成器开始-----
开始循环第1次
5
第1循环结束
开始循环第2次
4

上述代码如果next方法执行四次,就会报出StopIteration错误。如下所示:

Traceback (most recent call last):
----生成器开始-----
开始循环第1次
  File "D:/untitled/test.py", line 20, in <module>
3
第1循环结束
开始循环第2次
2
第2循环结束
开始循环第3次
1
    print (a.__next__())
第3循环结束
----生成器结束----
StopIteration

生成器中还有两个很重要的方法:send()和close()。

send(value):next()方法可以恢复生成器状态并继续执行,其实send()是除next()外另一个恢复生成器的方法。与next不同的是,它需要传入一个参数,一般传入None,即send(None)与next()是等效的。

close():这个方法用于关闭生成器,对关闭的生成器后再次调用next或send将抛出StopIteration异常。

posted @ 2018-09-19 21:39  龙~白  阅读(327)  评论(0编辑  收藏  举报