python中可迭代对象,迭代器,生成器

1. 可迭代对象:

在Python中,通过使用for...in...语句进行循环的对象,就是可迭代对象(Iterable)。通常使用isinstance来进行判断。

In [4]: from collections import Iterable

In [5]: for i in "Python":
   ...: print(i)
   ...:     
P
y
t
h
o
n
In [7]: a = [100,200,300]

In [8]: for i in a:
   ...: print(i)
   ...:     
100
200
300

通过isinstance()来看,返回都是True

In [13]: from collections import Iterable
In [11]: isinstance("python",Iterable)
Out[11]: True

In [12]: isinstance(a,Iterable)
Out[12]: True

下面看一个不是可迭代对象的例子:

In [13]: b =200

In [14]: for i in b:
    ...: print(i)
    ...:     
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
 in ()
----> 1 for i in b:
      2 print(i)
      3 

TypeError: 'int' object is not iterable

In [15]: isinstance(b,Iterable)
Out[15]: False

看报错说明,整型并不是一个可迭代对象。通过isinstance()看返回False。 
通过dir()查看发现可迭代对象都有__iter__方法。 
总结:含有__iter__方法的对象都是可迭代对象。

2. 迭代器

迭代器是指可以使用next()方法来进行回调的对象,可以通过使用iter()方法,将一个可迭代对象转换成迭代器。在next()方法进行调用时,如果没有可回调的对象时,就会抛出一个StopIteration异常。

In [22]: a
Out[22]: [100, 200, 300]

In [23]: b = iter(a)

In [24]: print(type(b))


In [25]: print(type(a))


In [26]: print(next(b))
100

In [27]: print(next(b))
200

In [28]: print(next(b))
300

In [29]: print(next(b))
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
 in ()
----> 1 print(next(b))

StopIteration:

可以通过isinstance(a,Iterator)来查看一个对象是否是迭代器。

In [31]: from collections import Iterable,Iterator

In [32]: a
Out[32]: [100, 200, 300]

In [33]: b
Out[33]: 

In [34]: isinstance(a,Iterable)
Out[34]: True

In [35]: isinstance(a,Iterator)
Out[35]: False

In [36]: isinstance(b,Iterable)
Out[36]: True

In [37]: isinstance(b,Iterator)
Out[37]: True
In [38]: dir(b)
Out[38]: 
['__class__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__iter__',
 '__le__',
 '__length_hint__',
 '__lt__',
 '__ne__',
 '__new__',
 '__next__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__setstate__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']

由此,可以总结出,迭代器必须含有__iter__,__next__方法。一个迭代器一定时一个可迭代对象,但是一个可迭代对象不一定是迭代器。

  • __next__ : 放回容器的下一个元素

  • __iter__: 返回迭代器本身

迭代器的优势:在构建迭代器是,不是将所有的元素一次性加载,而是等调用next方法时返回元素,所以这占用内存很少。

自定义一个迭代器:

class CountDown:
    """创建一个迭代器"""
    def __init__(self, step):
        self.step = step

    def __next__(self):
        """返回容器下一个元素"""
        if self.step <= 0:
            raise StopIteration
        self.step -= 1
        return self.step

    def __iter__(self):
        """返回迭代器本身"""
        return self

for element in CountDown(5):
    print(element)

结果:

4
3
2
1
0

使用迭代器来迭代Fibonacci数:

# coding=utf-8
class FibIterator(object):
    def __init__(self,n):
        self.n = n
        self.current = 0
        self.num1 = 0
        self.num2 = 1

    def __next__(self):
        if self.current < self.n:
            self.num1 , self.num2 = self.num2, self.num1 + self.num2
            self.current += 1
            return self.num1
        else:
            raise StopIteration



    def __iter__(self):
        """返回迭代器本身"""
        return self


if __name__ == "__main__":
    fib = FibIterator(10)
    for num in fib:
        print(num,end=" ")

结果:

1 1 2 3 5 8 13 21 34 55

3. 生成器:

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,因此可以采用一种更简便的语法,即生成器(generator)。 
生成器是一种特殊的迭代器,可以让编写返回元素序列的函数所需要的代码变得简单,高效,并不是像创建迭代器代码一样冗长。 
例子:Fibonacci数列使用生成器实现

def fib(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        a, b = b, a + b
        yield a
        current_num += 1


obj = fib(10)
for num in obj:
    print(num,end=" ")

结果:

1 1 2 3 5 8 13 21 34 55 89

生成器函数:

生成器函数和常规的函数都是使用def语句进行定义,但是器是基于yield指令返回一个值。可以暂停一个函数并返回中间结果。当需要一个将返回一个序列或在循环中执行的函数时,就可以使用生成器,因为当这些元素被传递到另一个函数中进行后续处理时,一次返回一个元素可以有效的提升整体性能。 
总结:

  • 使用了yield关键字的函数就是生成器。

  • yield的两个作用:1.保存当前的运行状态,然后暂停执行,即生成器(函数)挂起。2. 将yield关键字后面表达式的值作为返回值返回,此时可以理解为return的作用。

  • 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器。

生成器表达式

生成式表达式是一种实现生成器的便捷方式,将列表推导式的中括号替换为圆括号。 
和列表推导式的区别:列表生成式可以直接创建一个表,但是生成器表达式是一种边循环边计算,使得列表的元素可以在循环过程中一个个的推算出来,不需要创建完整的列表,从而节省了大量的空间。

In [43]: a = [ x*2 for x in range(5)]

In [44]: a
Out[44]: [0, 2, 4, 6, 8]

In [45]: b = ( x*2 for x in range(5))

In [46]: b
Out[46]:  at 0x7fc7961cd5e8>

In [47]: next(b)
Out[47]: 0

In [48]: next(b)
Out[48]: 2

In [49]: next(b)
Out[49]: 4

In [50]: next(b)
Out[50]: 6

In [51]: next(b)
Out[51]: 8

In [52]: next(b)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
 in ()
----> 1 next(b)

StopIteration: 
In [55]: b = ( x*2 for x in range(5))

In [56]: for i in b:
    ...: print(i)
    # 没有值,因为之前已经遍历完了。

总结:生成器是一种高级迭代器。生成器的优点是延迟计算,一次返回一个结果,这样非常适用于大数据量的计算。但是,使用生成器必须要注意的一点是:生成器只能遍历一次