一、可迭代对象:有_ _iter_ _方法

二、迭代器:同时有 _ _iter_ _和 _ _next_ _方法

三、生成器 :yield / (i for in in range(n))

四、字典、元祖、列表等可迭代对象

       可以被迭代若干次

       可以被转换成迭代器对象(使用iter方法)

 

一、可迭代对象

首先来看一下,python 2.7版本range的实现原理:

from typing import Iterable


def range_test(stop):
    start = 0
    result = []
    while start < stop:
        result.append(start)
        start += 1
    return result


if __name__ == '__main__':
    print(isinstance(range_test(10),Iterable))  # 判断返回结果是不是可迭代,True
    print(range_test(10))                     # output:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

这种方式,需要多少数据,全部存在list中,在数据量比较大的情况下,是非常耗内存的。

那么如何实现高性能(省内存)的呢?请看实现:

省内存:延迟生成数据,节省内存

from typing import Iterable


# Next含有__next__()方法,每次变量+1,一次只占用一个数的内存
class Next(object):
    """Next class"""

    def __init__(self, stop, start=-1):
        self.start = start
        self.stop = stop

    def __next__(self):
        if self.start >= self.stop - 1:
            raise StopIteration
        self.start += 1
        return self.start


# 含有__iter__()方法,return调用Next
class MyRange:

    def __init__(self, stop):
        self.stop = stop

    def __iter__(self):
        return Next(self.stop)


if __name__ == '__main__':
    print(Next(10).__next__())  # 0

print(isinstance(MyRange(10), Iterable)) # True (因为MyRange有__iter__()) print(isinstance(Next(10), Iterable)) # False (Next没有__iter__()) for i in MyRange(10): # for循环自动执行__iter__,__next__方法 print(i) # 0,1,2,3,4,5,6,7,8,9 # 下面用while循环,解释下运行的原理。(for的原理,for是一种语法,会自动执行__next__()) num = MyRange(10).__iter__() i = 0 while i < 10: print(num.__next__()) # 0,1,2,3,4,5,6,7,8,9 i += 1

 

总结:

1.可迭代对象:对象必须要有_ _iter_ _()方法

(能被for迭代的对象 都是可迭代对象):这里给你们捋一捋思路,能够被for迭代是因为for会自动调用__iter__(),那有了__iter__,就符合可迭代对象的基本定义了。

常见的可迭代对象:list,str,tuple

我们也可以用dir()自省,来判断某个每个对象是不是可迭代对象。自省的结果中有_ _iter_ _(),说明是可迭代对象

 

上面的例子可以用hasattr()验证,返回值是bool类型,可迭代对象是否有_ _iter_ _():--------hasattr() 函数用于判断对象是否包含对应的属性

 print(hasattr(list,"__iter__")) # True
 print(hasattr(str, "__iter__")) # True
 print(hasattr(tuple, "__iter__")) # True

 

2.for 用在迭代的场景,能用for,就不用while。不会越界,比较安全。(for 循环不会越界,while循环很容易越界,while通常用在自由执行循环的场景

   

 

二、迭代器

内置函数next():

首先来看下next()源码:

def next(iterator, default=None): # real signature unknown; restored from __doc__
    """
    next(iterator[, default])
    
    Return the next item from the iterator. If default is given and the iterator
    is exhausted, it is returned instead of raising StopIteration.
    """
    pass

看见没?想要使用next()函数,参数必须是个迭代器!!!

来看看list,能不能使用next()方法:

print(next([1,2]))
"""
    print(next([1,2]))
TypeError: 'list' object is not an iterator
"""

哎呀,报错了:list对象不是一个迭代器!!

因为next([1,2])实际上执行的是[1,2]._ _next_ _(). 但是list没有_ _next_ _()属性,就会报错(list不是一个迭代器)

print(hasattr(list,"__next__")) # False

 

那什么是迭代器??

迭代器:

必须同时有 _ _iter_ _和 _ _next_ _方法,才是迭代器

核心:通过_ _next_ _方法记住迭代的位置,有没有迭代完毕。

举例:上面例子中的class Next,里面有_ _next_ _,是不是迭代器呢?

class Next(object):
    """Next class"""

    def __init__(self, stop, start=-1):
        self.start = start
        self.stop = stop

    def __next__(self):
        if self.start >= self.stop - 1:
            raise StopIteration
        self.start += 1
        return self.start


if __name__ == '__main__':
    print(isinstance(Next(2),Iterator))  #False 因为虽然含有__next__属性,但是没有__iter__属性,不是一个迭代器

栗子2:

class Fruit:
    """ class Fruit
    :param
    :raise Raise StopIteration if index out of len(friuts)
    """

    def __init__(self):
        self.fruit = ["apple","peach","banana","grape"]
        self.index = 0

    def __next__(self):
        if self.index >= len(self.fruit):
            raise StopIteration
        self.index += 1
        return self.fruit[self.index - 1]

if __name__ == '__main__':
    fruit = Fruit()
    for item in fruit:      #对fruit对象进行迭代,不是一个可迭代对象,因为没有_ _iter_ _
        print(item)
"""
    for item in fruit:
TypeError: 'Fruit' object is not iterable
"""

下面加上一个_ _iter_ _方法,再来看一下

class Fruit(object):
    """ class Fruit
    :param
    :raise Raise StopIteration if index out of len(friuts)
    """

    def __init__(self):
        self.fruit = ["apple", "peach", "banana", "grape"]
        self.index = 0

    def __iter__(self):
        return iter(self.fruit)

    def __next__(self):
        if self.index >= len(self.fruit):
            raise StopIteration
        self.index += 1
        return self.fruit[self.index - 1]


if __name__ == '__main__':
    ft = Fruit()
    for item in ft:
        print(item)
"""
apple
peach
banana
grape
"""
print(isinstance(ft,Iterator)) # True 是迭代器,同时含有__iter__ 和__next__

iter()方法-----iter() 函数用来把可迭代对象生成迭代器

上面的_ _iter_ _()方法的返回值要是一个“迭代器”---严格的说,返回值必须有_ _next_ _()方法,比如开始的例子,MyRange中的_ _iter__()的返回值,调用了Next,有_ _next_ _()方法。

 总结:

①迭代器是一个特殊的可迭代对象,特殊在它还有_ _next_ _()方法

 

 

注意:上面的 _ _next_ _() 没有被for循环调用,for循环只是自动调用了_ _iter__()。因为自己写的__iter__(),不会去调用_ _next_ _()

那么怎么才能调用_ _next_ _()方法呢?

继承迭代器(直接继承迭代器(已经实现了_ _iter_ _),不需要自己写_ _iter__()方法了)

from typing import Iterator


class Fruit(Iterator):
    """ class Fruit
    :param
    :raise Raise StopIteration if index out of len(friuts)
    """

    def __init__(self):
        self.fruit = ["apple", "peach", "banana", "grape"]
        self.index = 0

    def __next__(self):
        print("这个是__next__ 方法")
        if self.index >= len(self.fruit):
            raise StopIteration
        self.index += 1
        return self.fruit[self.index - 1]


if __name__ == '__main__':
    ft = Fruit()
    for item in ft:
        print(item)
"""
这个是__next__ 方法
apple
这个是__next__ 方法
peach
这个是__next__ 方法
banana
这个是__next__ 方法
grape
这个是__next__ 方法
"""

 for 迭代迭代器时,会自动调用_ _next_ _()方法。自己写的那个算个假迭代器,不会执行_ _next_ _().

在实际使用中,直接继承迭代器即可。

总结:

①迭代器的_ _next_ _(),记住迭代的位置

②一次占用一个数的内存,省内存

③for原理:for循环自动调用_ _iter_ _(),而 _ _iter_ _()会调用 _ _next_ _().  ----------------核心

 

 

 

三、生成器

因为迭代器手写比较麻烦,所以python提供了生成器对象。

生成器对象的目的就是获得迭代器对象。

生成器的目的:就是获得迭代器对象(高效)

 

1.如何创建一个生成器对象?----使用yield

yield关键字----特殊功能,为了生成器而提供的关键字。

即函数中使用yield,调用函数的时候,会先 生成一个生成器(而不是直接运行函数)

 ❤生成器是特殊的迭代器,迭代器是特殊的可迭代对象。(生成器是可迭代对象)

def fun():
    print("在yield1前面")
    yield 1
    print("在yield1后面,yield2前面")
    yield 2


if __name__ == '__main__':
    # 验证是否是生成器
    print(fun())   # <generator object fun at 0x104d00150> 结果是一个生成器对象,但是没有运行函数
    print(hasattr(fun(),"__iter__"))  # True
    print(hasattr(fun(),"__next__"))  # True

    # 运行生成器,必须要调用__next__
    print(fun().__next__())  # 1 / 在yield1前面
    
    # 运行出全部结果,for自动调用__next__
    for i in fun():
        print(i)
"""
在yield1前面
1
在yield1后面,yield2前面
2
"""

当python解释器看到函数中有yield的时候,不会立即去调用这个函数,而是先创建一个生成器对象。而这个生成器对象会记住函数的状态(存代码,记住函数的执行状态:是否执行?执行到哪里了?)。

②原理解释:在for循环中,运行到第一个yield时,就暂时退出了(类似return,但区别是return是永久跳出不会再回去了)。再次运行时(__next__记住位置),会从yield1后面开始运行。

③ 当执行next(生成器对象)时,会执行生成器对象的yield.

 

     h = fun()  # h是一个生成器对象

  next(h),会执行g里面的yield

 ④直接调用函数,函数没有直接运行,而是先创建一个生成器对象?那什么时候才能运行yield呢?

    ❤调用_ _next_ _()方法是会运行yield。如上面例子中运行的for,for会自动调用_ _next_ _()。或者fun()._ _next_ _() ;

 ⑤生成器 就是 迭代器的抽象

 ⑥生成器的使用场景:协程 

读写大文件

(一边下载video 一边播放。下载的内容用yield 返回,记录下载返回的数据状态)

⑦生成器 省 内存的原理:就是因为__next__

   生成器是为了快速获得含有_ _next_ _()和_ _iter_ _()方法 ----就是为了获得迭代器。

   

 summary:

1.可迭代对象(含有__iter__)

2.迭代器的特点:

   记住遍历位置

   迭代器只能被迭代一遍

   同时含有__next__() 和__iter__()

3.迭代器的应用场景:for循环

4.有了循环为啥还要迭代器?循环需要判断便利到哪里,但是迭代器不需要判断

5.什么对象可以转化为迭代器?可迭代对象,用iter()进行转化

6.生成器(特殊的迭代器):

    ---简化迭代器创建过程,提供关键字yield 

    ---函数中有yield,调用函数时,函数会先创建一个生成器对象,这个对象就是一个迭代器

    ---yield 记住执行的位置;保存状态、恢复状态