Python之容器、迭代器、生成器

1.容器、可迭代对象、迭代器、生成器概念

1. 容器:存储许多元素的数据结构。通常存储在内存(迭代器、生成器是特例)可以使用in来判断某个元素是否在存在的对象都是容器

举个例子:容器就像一个箱子,里面可以存放许多东西,我可以往这个箱子存取东西,可以判断这个箱子是否有某样东西

2.可迭代对象:可以使用iter()变成迭代器的对象都是可迭代对象,大部分容器都是可迭代对象(str,set,list,tuple,打开状态的files,sockets等等)

3.迭代器:它是一个带状态的对象,保存当前记录状态,当使用next()函数调用时,可以返回容器的下一个值。如果容器中没有更多元素了,则抛出StopIteration异常.

任何实现了__iter__() and next()方法的对象都是迭代器,__iter__返回迭代器自身,__next__对象返回下一个值。生成器是另一种形态的迭代器

迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。比如itertools函数返回的都是迭代器对象。

4.生成器:使用yield关键字,python都会把这个函数视为一个生成器.它返回了一个可迭代的对象,可以使用next()访问它里面的值了

5.如何使用迭代器呢?

1.方式是使用for 。。 in 。语法糖结构来遍历迭代器。
for默认的会调用__iter__返回一个迭代对象,然后调用next()方法调用迭代对象的__next__方法获取下一个值。
如果最后没有值了,迭代器会抛出一个异常,for会捕捉处理这个异常,退出循环.

2.先获取迭代器对象,如使用it = iter(list)方法,在调用next(it)获取下一个值。最后没值了会抛出异常(StopIteration)

下面一张图来说明他们之间的关系

image

2.具体实例来演示上面的内容

以斐波那契数列来举例

1.普通的函数实现

# 普通函数斐波那契数列
def fib1(n):
    pre, cur = 0, 1
    i = 0
    while i < n:
        print(cur, end=' ')
        pre, cur = cur, cur + pre
        i += 1

调用: fib1(10)
输出:1 1 2 3 5 8 13 21 34 55

2.普通的函数实现改进

# 普通函数斐波那契数列改进
def fib1(n):
    result = []
    pre, cur = 0, 1
    i = 0
    while i < n:
        result.append(cur)
        pre, cur = cur, cur + pre
        i += 1
    return result
    
调用:lst = fib1(10)
      print(lst)
输出:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

3.自定义一个可迭代的类来实现斐波那契数列

class Fib:

    def __init__(self, stop):
        self.stop = stop
        self.pre = 0
        self.cur = 1
        self.start = 0
        print('init finish')

    def __iter__(self):
        print('return iter')
        return self

    def __next__(self):
        print('return next value')
        if self.start < self.stop:
            self.start += 1
            x = self.cur
            self.pre, self.cur = self.cur, self.cur + self.pre
            return x
        else:
            raise StopIteration()

初始化类,得到一个类实例对象f,可以看到"init finish"打印出来了
In [1]: f = Fib(10)
Out[1]:init finish

我们知道一个可迭代对象可以通过iter()函数转换成一个迭代器,
从下面的调用我们可以看出,当我们使用iter()函数时,它默认去调用函数的__iter__()函数,返回迭代对象
In [2]: it = iter(f)
Out[2]:return iter

当我们获取了迭代对象,我们调用next()函数来获取我们想要的值,
那么next()有做了哪些动作呢
1.它调用类中的__next__()方法
2.我们__next__()方法,返回当前值,并保存当前值记录状态和算出下一次调用要返回的值
In [3]: next(it)
Out[30]:return next value
Out[30]: 1

In [3]: next(it)
Out[30]:return next value
Out[30]: 2

如果一直执行next(),它会一直返回值,直到抛出StopIteration()异常

4.使用生成器

使用生成器,我们有两种形式:
1.创建生成器函数,使用yield关键字
2.使用生成器表达式

4.1创建生成器函数

# 使用yield实现斐波那契数列
def fib(n):
    pre, cur = 0, 1
    i = 0
    while i < n:
        print('call this')
        yield cur
        pre, cur = cur, pre + cur
        i += 1
        print('here')

从下面的执行结果我们可以看出,使用关键字yield后,调用fib(10)它的返回值是一个生成器
In [33]: f = fib(10)
In [34]: type(f)
Out[34]: generator

从上面图我们知道generator is iterator,我们可以使用next()函数获取值,可以看出我们没执行一次next(),
好像我们的程序就在yield返回cur,然后停到这不执行,直到下次调用next()函数又继续往下走,不停重复这个过程
In [1]: next(f)
call this
Out[1]: 1

In [2]: next(f)
here
call this
Out[2]: 2

In [3]: next(f)
here
call this
Out[3]: 3

4.2生成器表达式实现斐波那契数列

# 从下面我们可以看出,使用()后a是一个generator类型
In [1]: a = ( x for x in fib3(10))
In [2]: type(a)
Out[2]: generator

In [3]: next(a)
call this
Out[3]: 1

In [4]: next(a)
here
call this
Out[4]: 1

In [5]: next(a)
here
call this
Out[5]: 2

5.对于可迭代对象遍历,我们一定会想到for循环

下面具体解释下for循环:

1.for循环的一般格式如下:
for iter_var in iterable:
    suite_to_repeat
从上面可以看出,只要是可迭代对象都可以使用
它的里面到底做了些什么呢?

举个例子吧,我们看看for循环它到底做了哪些工作

class Fib:

    def __init__(self, stop):
        self.stop = stop
        self.pre = 0
        self.cur = 1
        self.start = 0
        print('init finish')

    def __iter__(self):
        print('return iter')
        return self

    def __next__(self):
        print('return next value')
        if self.start < self.stop:
            self.start += 1
            x = self.cur
            self.pre, self.cur = self.cur, self.cur + self.pre
            return x
        else:
            raise StopIteration()
           
对一个可迭代对象进行迭代操作
def fortest():
    for x in Fib(4):
        print(x)

fortest()


import dis
dis.dis(fortest)

打印以下信息:
init finish
return iter
return next value
1
return next value
1
return next value
2
return next value
3
反编译结果
108           0 SETUP_LOOP              24 (to 26)
              2 LOAD_GLOBAL              0 (Fib)
              4 LOAD_CONST               1 (4)
              6 CALL_FUNCTION            1
              8 GET_ITER
        >>   10 FOR_ITER                12 (to 24)
             12 STORE_FAST               0 (x)

109          14 LOAD_GLOBAL              1 (print)
             16 LOAD_FAST                0 (x)
             18 CALL_FUNCTION            1
             20 POP_TOP
             22 JUMP_ABSOLUTE           10
        >>   24 POP_BLOCK
        >>   26 LOAD_CONST               0 (None)
             28 RETURN_VALUE

可以看出在for内部有两个很重要的指令GET_ITER,FOR_ITER,
GET_ITER相当于调用iter(Fib(4),返回对象迭代器it
FOR_ITER相当于调用next(it),调用类内部的__next()__()方法获取值,一直循环这个过程,直到遇到抛出的StopIteration()异常
posted @ 2018-03-24 18:11  孤寂的狼  阅读(242)  评论(0编辑  收藏  举报