zoukankan      html  css  js  c++  java
  • 可迭代(Interable),迭代器(Iterator),生成器(generator)的手记(11月26日再次修改)

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

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

    可迭代对象只要有__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,携程的时候要用。


  • 相关阅读:
    微信小程序购物商城系统开发系列-工具篇
    如何用js获取浏览器URL中查询字符串的参数
    Vue.js——vue-resource全攻略
    多个 ng-app 中 Controllers & Services 之间的通信
    前端分页功能的实现以及原理
    纯css实现轮播图
    最好的Angular2表格控件
    2017年要学习的三个CSS新特性
    Kafka数据安全性、运行原理、存储
    Hbase与hive集成与对比
  • 原文地址:https://www.cnblogs.com/sidianok/p/11795975.html
Copyright © 2011-2022 走看看