Python3 迭代对象和迭代器的区别,深刻理解__iter__和__next__ 迭代器的原理(自定义迭代器方法进行)

迭代器和生成器都是很重要的知识,因为在后边的同步异步都要用到.

python的迭代协议构成了迭代器.

迭代器是访问集合内元素的一种方式,一般用来遍历数据.

要记住 迭代器和 下标的访问方式不一样,迭代器没有返回值,它取值的方式就是用next().

迭代器提供了一种惰性访问数据方式,访问数据时候才能计算和返回数据.

list 的访问方式是用 getitem 协议,
list 和迭代器 可以用for 循环都是实现迭代协议(iter)这个方法.

我们查找下源码 :

from collections.abc import Iterable,Iterator
class Iterable(metaclass=ABCMeta):
    __slots__ = ()
    @abstractmethod
    def __iter__(self):
        while False:
            yield None

只要有这个 __iter__抽象魔法函数一定可迭代, 咱们再看下Iterator

class Iterator(Iterable):
    __slots__ = ()
    def __iter__(self):
        return  self
     @abstractmethod
    def __next__(self):
        raise StopIteration

大家看下 Iterator 比Iterable 多了一个抽象魔法函数 next,
并且继承了Iterable也重写了iter 抽象方法.

其中 iter 是返回个迭代对象,next 返回下一个数据,在我们访问数据的时候就变成一个迭代器,

迭代器只是比迭代对象多了抽象魔法函数 next, 要记住迭代器也是迭代对象.

咱们 看下 list 源码 :

class list(object):
    """
    list() -> new empty list
    list(iterable) -> new list initialized from iterable's items
    """
    def append(self, p_object): # real signature unknown; restored from __doc__
        """ L.append(object) -> None -- append object to end """
        pass

    def __iter__(self, *args, **kwargs): # real signature unknown
        """ Implement iter(self). """
        pass

list 只重写了 iter 这个魔法函数,所以list 只是个可迭代对象.

具体有什么区别,我们看下边的例子:

>>> x = [1,2,3]
>>> y = iter(x)  #inter是特殊的方法可以把迭代对象变成迭代器,可以查下
>>> z = iter(x)
>>> next(y)   #通过inter方法变成迭代器之后就可以用next方法,如果用 next(x)会报错
1
>>> next(y)
2
>>> next(z)
1
>>> type(x)
<class 'list'>
>>> type(y)   #inter是特殊的方法可以把迭代对象变成迭代器,可以查下
<class 'list_iterator'>

 

上边的iter 和next 与魔法函数的__inter__ 和__next__ 是不一样的,自己可以查下.
只要迭代对象调用了next方法就可以变成迭代器.
迭代器是可以循环输出,直到报错为止.

有人说了 list 是个迭代对象,为什么可以用for循环.

for i in [0,2,3]:
    print(i)

1.首先list 是迭代对象,它只是重写了iter魔法函数,
2.那为什么可以循环输出呢,答案是for 循环自带next 魔法函数

咱们debug 看下:

Iterator = {ABCMeta} <class 'collections.abc.Iterator'>
Iterable = {ABCMeta} <class 'collections.abc.Iterable'>

 

list 用到了for循环的 next,变成个迭代器可以循环输出.

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

就相当于 :

a=iter([1,2,3,4])

while True:

    try:
      x = next(a);
    except StopIteration:
        break
    print(x)

先调用了iter 从迭代对象变成一个迭代器,然后调用了next 方法.进行遍历.

 

 

1.iter 的用法

咱都知道, list ,tuple,dict 都是可迭代对象,如果想让他们转化成迭代器.
我们可以这么做,调用 inter()方法,它会返回一个迭代器.
例如:

from collections import Iterable,Iterator

a=[1,2]
b=(1,)
c={"name":"Andy"}

print(type(a),type(b),type(c))   #<class 'list'> <class 'tuple'> <class 'dict'>

print(isinstance(a,Iterable),isinstance(b,Iterable),isinstance(c,Iterable))  #True True True
print(isinstance(a,Iterator),isinstance(b,Iterator),isinstance(c,Iterator)) #False False False
a_iterator=iter(a)
b_iterator=iter(b)
c_iterator=iter(c)

print(isinstance(a_iterator,Iterable),isinstance(b_iterator,Iterable),isinstance(c_iterator,Iterable)) #True True True

 

2.getitem 也是模拟的返回一个迭代器

前面我讲过 getitem 这个魔法函数,它可以改变对象类型变成一个iterable ,也就是迭代器

例如:

class Person:
    def __init__(self,persion_list):
        self.persion_list=persion_list
    def __getitem__(self, item):
        return self.persion_list[item]
 
body=Person(["Xiuwu","Adny","Maggie"])

for i in body:
    print (i)

 

上边的代码我们用for循环的时候, 实际上就用了iter()方法 ,不理解可以看看我的上篇写作.

然后看下如下代码

class Person:
    def __init__(self, persion_list):
        self.persion_list = persion_list
    def __iter__(self):
        return  1
    def __getitem__(self, item):
        return self.persion_list[item]


body = Person(["Xiuwu", "Adny", "Maggie"])

body=iter(body)

 

打印结果:

 iter() returned non-iterator of type 'int'

 

1.说明 用iter(body)的时候首先找到 ——iter__方法,由于我们返回的不是的个iterator所以报错,
2. __iter__方法找不到,它会继续找 是否有模拟返回迭代器呢,
3. 如果你把__iter__注销掉,它会找到——getitem——

咱们测试下,是否getitem 是否实现iterable ,代码如下:

from collections import Iterable, Iterator


class Person:
    def __init__(self, persion_list):
        self.persion_list = persion_list
    #def __iter__(self):
     #   return  1
    def __getitem__(self, item):
        return self.persion_list[item]


body = Person(["Xiuwu", "Adny", "Maggie"])

print(iter(body))     #<iterator object at 0x0000000002242AC8>
print(isinstance(iter(body),Iterator) #True     说明getitem是可以转化为iterable

如果把getitem 也注销,按道理应该body 不是iterable了,试试
注销之后再运行报错如下:

TypeError: 'Person' object is not iterable

 

证明咱们的想法是对的.

3.分析原理后,咱们就自己写个迭代器.

前面我们已经知道了,迭代器需要重写两个方法,如下:

class Myiterator:
    def __iter__(self):
        pass
    def __next__(self):
        pass 

 

如果继承了 Iterator类 ,iter 方法 可以删除.如下:

class Myiterator(Iterator):
    def __next__(self):
        pass 

 

这个__next__ 正是我们要重写的取值逻辑

完成的代码如下,解释请看备注:

from collections import Iterable, Iterator

class Person:
    def __init__(self, persion_list):
        self.persion_list = persion_list
    def __iter__(self):
        return  Myiterator(self.persion_list)   #调用我们重写的迭代器方法
    #def __iter__(self):
     #   return  1
    #def __getitem__(self, item):   #把这个方法用我们自己写的迭代器方法替代.
     #   return self.persion_list[item]  
class Myiterator(Iterator):         #继承Iterator 就不需要写__iter__,直接调用父类的.
    def __init__(self,persion_list):
        self.persion_list=persion_list  
        self.index=0      #由于iterator 是没有index 的,这个要我们手动添加
    def __next__(self):   #这个就是迭代器的取值逻辑 
        while True:      #当为false 的时候结束循环
            try:
                word = self.persion_list[self.index]      #取值动作 
            except IndexError:   #index 当变得超出persion_list 会报错的 ,先抓住这个异常
                raise StopIteration     #迭代到没有值要用这异常,咱们把异常做个转化 
            self.index = self.index + 1   #递增我们的index
            return word   #返回取到的值


body = Person(["Xiuwu", "Adny", "Maggie"])

a=iter(body)    #调用我们自定义的迭代器方法 
 print(a)   # <__main__.Myiterator object at 0x00000000022399B0>  
               #从打印结果看说明我们自定义的迭代器方法生效了,已经返回一个迭代器

print(next(a))  #Xiuwu  
                       #从打印结果我们判断出 __next__生效,如果想循环输出,自己试试for 循环.



摘自:https://blog.csdn.net/qq_34979346/category_8322329.html
posted @ 2021-01-27 14:57  云long  阅读(582)  评论(0编辑  收藏  举报