可迭代(Interable),迭代器(Iterator),生成器(generator)的手记(11月26日再次修改)

2020年12月26日 0点22分 更新基础信息

Python基础教程:书P158页

实现了方法__iter__的对象是可迭代的,而实现了方法__next__的对象是迭代器。

这两个方法本质上面没有任何的联系。

 

 

今天既然看到这里了,就做个笔记。这个玩意已经花过我很多时间,其实一开始学还好没去纠结它,要不然死的惨兮兮了,网上资料一大堆,反正我是没找到能看懂的,

如果你有机会看到我写的,还是没看懂,请邮件通知我,哪里没看懂,我再写仔细点,至少我觉的,这一块知识,我是可以完全拿下了。

可迭代对象只要有__iter__属性的都可以称呼可迭代(Interable)。

先说明,可迭代对象,上面已经定义了什么是可迭代对象,至少我觉的这句话非常的抽象,简单的来说,就是能被for循环当做对象的,就是可迭代对象。

因为能被for循环,里面一定要有__iter__方法

 

In [119]: import collections.abc                                                                          

In [120]: a = dict()                                                                                      

In [121]: b = list()                                                                                      

In [122]: c = set()                                                                                       

In [123]: d = ''                                                                                          

In [124]: f = range(10)                                                                                   

In [125]: isinstance(a, collections.abc.Iterable)                                                         
Out[125]: True

In [126]: isinstance(b, collections.abc.Iterable)                                                         
Out[126]: True

In [127]: isinstance(c, collections.abc.Iterable)                                                         
Out[127]: True

In [128]: isinstance(d, collections.abc.Iterable)                                                         
Out[128]: True

In [129]: isinstance(f, collections.abc.Iterable)                                                         
Out[129]: True

In [130]: '__iter__' in dir(f)                                                                            
Out[130]: True

In [131]: '__iter__' in dir(d)                                                                            
Out[131]: True

In [132]: '__iter__' in dir(a)                                                                            
Out[132]: True

 通过collections.abc.Iterable(可迭代对象的基类),可以判断出,我上面写的那些对象都是可迭代对象,而且里面都有__iter__方法。

那我现在自己定义一个可迭代对象:

In [137]: class My_Iterable: 
     ...:     def __iter__(self):
     ...:         print('我是可迭代对象')
     ...:         pass
     ...:                                                                                                 

In [138]:                                                                                                 

In [138]: it = My_Iterable()                                                                              

In [139]: isinstance(it, collections.abc.Iterable)                                                        
Out[139]: True

In [140]: for i in it:
     ...:     print(i)
     ...:                                                                                                 
我是可迭代对象
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-140-d3d1294ef949> in <module>
----> 1 for i in it:
      2     print(i)
      3

TypeError: iter() returned non-iterator of type 'NoneType'

In [141]:                             

 从上面的代码里面可以看到,我自己定义了一个可迭代对象,这是一个可迭代对象,也能被for循环调用,但因为我的__iter__返回了不是迭代器,所以报错了。

注意这局提示:TypeError: iter() returned non-iterator of type 'NoneType',但明显我__iter__里面的print输出被执行了。

这些列子可以证明了前面说的只要有__iter__就是可迭代对象,能被for循环调用。

 

这里有一个参考资料,为什么for循环要调用___iter__:https://www.zhihu.com/question/44015086/answer/119281039

 

 

迭代器只要拥有__iter__与__next__属性就是迭代器(Iterator)。

概念又是非常抽象的玩意,这个有点不好用通俗的书,初学者可以简单的认为既能被for循环调用,又能被next函数调用的对象就是迭代器。

Python的内置对象中,基本没有现成的迭代器,当然可以通过iter方法产生迭代器(后面讲),还有一个就是用生成器的生成式。

In [144]: class My_Iterator: 
     ...:     def __iter__(self): 
     ...:         print('我是可迭代对象') 
     ...:         pass 
     ...:     def __next__(self): 
     ...:         pass 
     ...:          
     ...:                                                                                                 

In [145]: my = My_Iterator()                                                                              

In [146]: isinstance(my, collections.abc.Iterator)                                                        
Out[146]: True

 上面定义了一个对简单的迭代器,就因为里面有了两个定义的属性(属性跟方法其实都差不多,可调用的属性就是方法)。

 

我们知道通过iter的函数可以将一个Python中的常见可迭代对象(字典,集合,字符串,元祖,列表等等)变成迭代器,其实这里函数再Python内部定义为

def iter(source, sentinel=None): # known special case of iter
    """
    iter(iterable) -> iterator
    iter(callable, sentinel) -> iterator
    
    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.
    """
    pass

 但具体的操作实在看不出来,根据我的实际来看,对象的__repr__输出肯定改变了,还有多了一些迭代器的方法,少了一些操作对象方法。简单用一个字符串来演示。

In [152]: string = 'sidian'                                                                               

In [153]: o_arr = dir(string)                                                                             

In [154]: string_iter = iter(string)                                                                      

In [155]: n_arr = dir(string_iter)                                                                        

In [156]: string                                                                                          
Out[156]: 'sidian'

In [157]: string_iter                                                                                     
Out[157]: <str_iterator at 0x10b20f390>

In [158]: o_arr                                                                                           
Out[158]: 
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmod__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isascii',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']

In [159]: set(o_arr)-set(n_arr)                                                                           
Out[159]: 
{'__add__',
 '__contains__',
 '__getitem__',
 '__getnewargs__',
 '__len__',
 '__mod__',
 '__mul__',
 '__rmod__',
 '__rmul__',
 'capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isascii',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill'}

In [160]: set(n_arr)-set(o_arr)                                                                           
Out[160]: {'__length_hint__', '__next__', '__setstate__'}

 

从上面的操作可以看到,从一个普通字符串(可迭代对象)通过函数iter变成一个迭代器,这里面其实应该执行了很多,同时发现变成了迭代器多一个关键属性__next__

记住一点,迭代器通过iter方法返回的还是他自己,所以一般的自定义迭代器__iter__一般返回自己,但也不是必须的,前面已经说出__iter__只要返回一个迭代器就好。

下面我定义一个编辑畸形的迭代器,通过for循环与next循环产生独立的不同的输出。

In [161]: class Sep_Iterator: 
     ...:     def __iter__(self): 
     ...:         return (i for i in range(5)) 
     ...:     def __next__(self): 
     ...:         return '666' 
     ...:                                                                                                 

In [162]: sep = Sep_Iterator()                                                                            

In [163]: for i in sep: 
     ...:     print(i) 
     ...:                                                                                                 
0
1
2
3
4

In [164]: next(sep)                                                                                       
Out[164]: '666'

In [165]: next(sep)                                                                                       
Out[165]: '666'

In [166]: next(sep)                                                                                       
Out[166]: '666'

 通过上面的执行更加可以看出其实__iter__与__next__是独立的。

在一般的迭代里面__iter__返回(self)自身,因为自身本来就是迭代器,当一个for循环调用该对象时,首相看对象的__iter__方法,返回自身,因为自身本来就时迭代器,符合__iter__的返回标准,然后循环调用对象__next__方法,只到遇到StopIteration停止。这时for循环调用一个迭代器的输出。

 

前面的参考链接链接里面也很具体说了for循环调用迭代器的过程,我的理解简单说下。for循环首先调用对象的__iter__方法,比如一个字符串,它马上生成一个新的字符串的迭代器,然后返回出来,通过迭代器里面的__next__循环取出对象里面的值。这就时为什么,一个Python内置的普通可迭代对象能够重复的被for循环使用,因为for循环在读取他的时候,他的__iter__默认给他搞了一个新的对象出来。

 

可迭代对象与迭代器的区别:

1,迭代器占用的内存特别小。

2,迭代器可以保存内部的状态。

In [168]: x1 = [i for i in range(1000000)]                                                                

In [169]: x2 = (i for i in range(1000000))                                                                

In [170]: x1.__sizeof__()                                                                                 
Out[170]: 8697440

In [171]: x2.__sizeof__()                                                                                 
Out[171]: 96

In [172]: next(x2)                                                                                        
Out[172]: 0

In [173]: next(x2)                                                                                        
Out[173]: 1

In [174]: next(x2)                                                                                        
Out[174]: 2

 

 

 

生成器(generator),可以用简单生成器(i,for i in range(10)),写法跟列表生成器样式通用,把[]换成(),

还有可以通过自定义方法用yield生成。

生成器肯定是迭代器,更加是可迭代对象,生成器的功能是最多的

生成器是一种特殊的迭代器,特殊在哪里,就是这个特殊迭代器在制作的时候不需要自己定义__iter__与__next__

想判断某个对象迭代器还是容器,可以拿该对象为参数,分别调用iter,如果返回的对象相同就时迭代器,容器对象每次返回的都是不同的对象

而且相对迭代器有三个生成器对象的专属方法:

  • send
  • throw
  • close

简单的来说,迭代器只能从对象里面取值,生成器可以互动了,你还可以向对象里面送值。

yield,send,throw,close。我这里不写了,篇幅很长。

可以参考:https://blog.csdn.net/jpch89/article/details/87036970

一般用的最多也就yield及send,携程的时候要用。


posted @ 2019-11-05 00:53  就是想学习  阅读(898)  评论(0编辑  收藏  举报