• python进阶之生成器


    迭代器

    什么叫迭代

    可以被for循环的就说明他们是可迭代的,比如:字符串,列表,字典,元祖,们都可以for循环获取里面的数据

    下面我们看一个代码:  

    1 number = 12345
    2 for i in number:
    3     print(i)
    4 输出:
    5 Traceback (most recent call last):
    6   File "D:**.py", line 272, in <module>
    7     for i in number:
    8 TypeError: 'int' object is not iterable

    报错信息是说:int类型不可迭代,不能使用循环取每个数据。 那么我们又怎么说 字符串,列表,字典,元祖是可迭代的呢?

     1 from collections import Iterable
     2 
     3 l = [1, 2, 3, 4]
     4 t = (1, 2, 3, 4)
     5 d = {1: 2, 3: 4}
     6 s = {1, 2, 3, 4}
     7 
     8 print(isinstance(l, Iterable)) # 判断是否是可迭代
     9 print(isinstance(t, Iterable))
    10 print(isinstance(d, Iterable))
    11 print(isinstance(s, Iterable))
    True
    True
    True
    True
    

    再从字面上理解一下,其实迭代就是我们刚刚说的,可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代

    什么叫可迭代协议 

    我们现在是从结果分析原因,能被for循环的就是“可迭代的”,但是如果正着想,for怎么知道谁是可迭代的呢?

    假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个一个的取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。

    可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。

    1 print(dir([1,2]))
    2 print(dir((1,1)))
    3 print(dir({1:2}))
    4 print(dir({1,2}))

    输出:

    ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
    ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']
    ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
    ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']
    

    现在我们可以知道:可以被for循环的都是可迭代的,要想迭代内部必须有一个__iter__方法。

    那么这个方法又干了些什么事情呢?

    print([1,2].__iter__())
    
    结果
    <list_iterator object at 0x1024784a8>
    

    看结果,应该是得到了一个可迭代对象list_iterator 就是一个迭代器,现在我们知道这个列表有一个迭代器了

    1 print(set(dir([1,2].__iter__()))-set(dir([1,2])))
    2 
    3 输出:
    4 {'__next__', '__setstate__', '__length_hint__'}

    我们获取了列表迭代器3个方法,那么这些方法又干了什么呢? 我们只说__next__

    1 iter = [1,2,3,4,5,6].__iter__()
    2 
    3 #一个一个的取值
    4 print(iter.__next__())
    5 print(iter.__next__())
    6 输出:
    7 1
    8 2

    我们看到的结果是取到了列表的前两个元素,所以说,for循环就是调用了内部的__next__方法实现遍历的,我们可以不使用for循环,直接调用这个方法就可以实现遍历列表元素

    但是如果我们列表有3个元素我们调用__next__4次就会抛出异常StopIteration,因为没有第4个元素

     1 iter = [1,2,3].__iter__()
     2 
     3 #一个一个的取值
     4 print(iter.__next__())
     5 print(iter.__next__())
     6 print(iter.__next__())
     7 print(iter.__next__())
     8 输出:
     9 Traceback (most recent call last):
    10 1
    11   File "D:/pythonSeleniumTestCode/pythonStu/python练习100例.py", line 295, in <module>
    12 2
    13     print(iter.__next__())
    14 3
    15 StopIteration

    现在我们把这个异常处理一下

    1 iter = [1,2,3].__iter__()
    2 while 1:
    3     try:
    4         item = iter.__next__()
    5         print(item)
    6     except StopIteration:
    7         break

    那现在我们就使用while循环实现了原本for循环做的事情,我们是从谁那儿获取一个一个的值呀?是不是就是l_iter?好了,这个l_iter就是一个迭代器。

    迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。

    现在我们已经大概有了迭代器的印象,那么我们再来看看生成器是个什么鬼!

    生成器

    我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是可以节省内存。

    如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器

    Python中提供的生成器

    1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

    2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

    生成器Generator

    本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)

    特点:惰性运算,开发者自定义

    看实例代码:

    1 def genrator():
    2     for i in range(1, 5):
    3         yield ('正在生成数字{}'.format(i)) # 
    4 
    5 yie = genrator()
    6 for i in yie:
    7     print(i)

    输出:

    正在生成数字1
    正在生成数字2
    正在生成数字3
    正在生成数字4
    

    如果我只想生成2个数字我们该怎么实现呢?是不是这样?

    1 yie = genrator()
    2 num = 0
    3 for i in yie:
    4     print(i)
    5     num+=1
    6     if num == 2:
    7         break

    输出:

    正在生成数字1
    正在生成数字2
    

    现在们已经生成了2个数字了,那么我想接着生成,还可不可以呢?

     1 def genrator():
     2     for i in range(1, 5):
     3         yield ('正在生成数字{}'.format(i))
     4 
     5 yie = genrator()
     6 num = 0
     7 for i in yie:
     8     print(i)
     9     num+=1
    10     if num == 2:
    11         print('我只能生成2个数')
    12         break
    13 for i in yie:
    14     print(i)

    输出:

    正在生成数字1
    正在生成数字2
    我只能生成2个数
    正在生成数字3
    正在生成数字4
    

    结果我们分析出,生成2个数以后既然还可以接着原来的生成。

    下面我们再来看看到底怎么使用生成器,我现在要监听一个文件的输入,如果文件中增加了数据,我就在控制到输出增加的内容

    import time


    def tail(filename):
    f = open(filename)
    f.seek(0, 2) #从文件末尾算起
    while True:
    line = f.readline() # 读取文件中新的文本行
    if not line:
    time.sleep(0.1)
    continue
    yield line

    tail_g = tail('tmp.txt')
    for line in tail_g:
    print(line)

    只要我再A文件中写入一行数据,那么控制到就会输出这行数据,我们就达到了监听文件的作用,是不是还挺好用的!

    结论

    生成器好处

    1.不会占用太多的内存,我们需要生成一个数就生成,不需要就不用叫他生成

    2.延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。

    什么是生成器

    只要含有yield关键字的函数都是生成器函数, 且yield不能与return一起使用,二者存一,而且只能写在函数的内部

  • 相关阅读:
    unity fbx 导出动画
    Unity正交模式摄像机与屏幕适配的方法
    unity3d 代码动态添加,修改BoxCollider2D
    Unity3D 移动摇杆处理
    protobuff 编译注意事项
    sendBroadcast无法接收消息可能原因
    FB相关
    上传速度慢
    CocosCreator与Laya2.0区别
    LayaBox 常用技巧
  • 原文地址:https://www.cnblogs.com/linuxchao/p/linux-generator.html
Copyright © 2020-2023  润新知