zoukankan      html  css  js  c++  java
  • python-迭代器,生成器

    一. 迭代器

            1. 可以使用dir()函数来检查类的内部定义的函数
             2.特点:
                __iter__() 获取迭代器
                __next__() 获取最前面这个元素  

      我们之前⼀直在⽤可迭代对象进⾏迭代操作. 那么到底什么是可迭代对象. 本⼩节主要讨
    论可迭代对象. ⾸先我们先回顾⼀下⽬前我们所熟知的可迭代对象有哪些:
      str, list, tuple, dict, set. 那为什么我们可以称他们为可迭代对象呢? 因为他们都遵循了可
    迭代协议. 什么是可迭代协议. ⾸先我们先看⼀段错误代码:

    # 对的
    s = "abc"
    for c in s:
        print(c)
    # 错的
    for i in 123:
        print(i)
    结果:
    Traceback (most recent call last):
        File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 8, in
    <module>
        for i in 123:
    TypeError: 'int' object is not iterable
    

      注意看报错信息中有这样⼀句话. 'int' object is not iterable . 翻译过来就是整数类型对象
    是不可迭代的. iterable表⽰可迭代的. 表⽰可迭代协议. 那么如何进⾏验证你的数据类型是否
    符合可迭代协议. 我们可以通过dir函数来查看类中定义好的所有⽅法: 

    s = "我的哈哈哈"
    print(dir(s)) # 可以打印对象中的⽅法和函数
    print(dir(str)) # 也可以打印类中声明的⽅法和函数
    

      在打印结果中. 寻找__iter__ 如果能找到. 那么这个类的对象就是⼀个可迭代对象 .

    ['__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', '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']
    

      我们发现在字符串中可以找到__iter__. 继续看⼀下list, tuple, dict, set

    print(dir(tuple))
    print(dir(list))
    print(dir(open("护⼠少妇嫩模.txt"))) # ⽂件对象
    print(dir(set))
    print(dir(dict))
    结果:
    ['__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']
    ['__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']
    ['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__',
    '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__',
    '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__',
    '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__',
    '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__',
    '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed',
    '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing',
    'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno',
    'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read',
    'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell',
    'truncate', 'writable', 'write', 'writelines']
    ['__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'] ['__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']

      我们发现这⼏个可以进⾏for循环的东⻄都有__iter__函数, 包括range也有. 可以⾃⼰试⼀
    .
    综上. 我们可以确定. 如果对象中有__iter__函数. 那么我们认为这个对象遵守了可迭代协议.
    就可以进⾏迭代. 这⾥的__iter__是帮助我们获取到对象的迭代器. 我们使⽤__next__()来获取
    到⼀个迭代器中的元素. 那么我们之前讲的for的⼯作原理到底是什么? 继续看代码

    s = "我爱北京天安⻔"
    c = s.__iter__() # 获取迭代器
    print(c.__next__()) # 使⽤迭代器进⾏迭代. 获取⼀个元素 我
    print(c.__next__()) # 爱
    print(c.__next__()) # 北
    print(c.__next__()) # 京
    print(c.__next__()) # 天
    print(c.__next__()) # 安
    print(c.__next__()) # ⻔
    print(c.__next__()) # StopIteration
    

      我们可以把要迭代的内容当成⼦弹. 然后呢. 获取到迭代器__iter__(), 就把⼦弹都装在弹夹
    . 然后发射就是__next__()把每⼀个⼦弹(元素)打出来. 也就是说, for循环的时候. ⼀开始的
    时候是__iter__()来获取迭代器. 后⾯每次获取元素都是通过__next__()来完成的. 当程序遇到
    StopIteration将结束循环

    . ⽣成器
      什么是⽣成器. ⽣成器实质就是迭代器.
      在python中有三种⽅式来获取⽣成器:

      1. 通过⽣成器函数
      2. 通过各种推导式来实现⽣成器
      3. 通过数据的转换也可以获取⽣成器

    今天, 我们主要使⽤⽣成器函数来完成⽣成器的创建和使⽤.
    ⾸先, 我们先看⼀个很简单的函数:

    def func():
        print("111")
        return 222
    ret = func()
    print(ret)
    结果:
    111
    222
    

      将函数中的return换成yield就是⽣成器

    def func():
        print("111")
        yield 222
    ret = func()
    print(ret)
    结果:
    <generator object func at 0x10567ff68>
    

      运⾏的结果和上⾯不⼀样. 为什么呢. 由于函数中存在了yield. 那么这个函数就是⼀个⽣成器
    函数. 这个时候. 我们再执⾏这个函数的时候. 就不再是函数的执⾏了. ⽽是获取这个⽣成器.
    如何使⽤呢? 想想迭代器. ⽣成器的本质是迭代器. 所以. 我们可以直接执⾏__next__()来执⾏
    以下⽣成器

    def func():
        print("111")
        yield 222
    gener = func() # 这个时候函数不会执⾏. ⽽是获取到⽣成器
    ret = gener.__next__() # 这个时候函数才会执⾏. yield的作⽤和return⼀样. 也是返回
    数据
    print(ret)
    结果:
    111
    222
    

      那么我们可以看到, yieldreturn的效果是⼀样的. 有什么区别呢? yield是分段来执⾏⼀个
    函数. return? 直接停⽌执⾏函数.

    def func():
        print("111")
        yield 222
        print("333")
        yield 444
    gener = func()
    ret = gener.__next__()
    print(ret)
    ret2 = gener.__next__()
    print(ret2)
    ret3 = gener.__next__() # 最后⼀个yield执⾏完毕. 再次__next__()程序报错, 也就是
    说. 和return⽆关了.
    print(ret3)
    结果:
    111
    Traceback (most recent call last):
    222
    333
    File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in
    <module>
    444
     ret3 = gener.__next__() # 最后⼀个yield执⾏完毕. 再次__next__()程序报错, 也
    就是说. 和return⽆关了.
    StopIteration
    

      当程序运⾏完最后⼀个yield. 那么后⾯继续进⾏__next__()程序会报错.
    好了⽣成器说完了. ⽣成器有什么作⽤呢? 我们来看这样⼀个需求. 老男孩向JACK JONES
    10000套学⽣服. JACK JONES就比较实在. 直接造出来10000套衣服.

    def cloth():
        lst = []
        for i in range(0, 10000):
            lst.append("⾐服"+str(i))
        return lst
    cl = cloth()
    

      但是呢, 问题来了. 老男孩现在没有这么多学⽣啊. ⼀次性给我这么多. 我往哪⾥放啊. 很尴尬
    . 最好的效果是什么样呢? 我要1. 你给我1. ⼀共10000. 是不是最完美的.

    def cloth():
        for i in range(0, 10000):
            yield "⾐服"+str(i)
    cl = cloth()
    print(cl.__next__())
    print(cl.__next__())
    print(cl.__next__())
    print(cl.__next__())
    

      区别: 第⼀种是直接⼀次性全部拿出来. 会很占⽤内存. 第⼆种使⽤⽣成器. ⼀次就⼀个. ⽤多
    少⽣成多少. ⽣成器是⼀个⼀个的指向下⼀个. 不会回去, __next__()到哪, 指针就指到哪⼉.
    下⼀次继续获取指针指向的值.

      接下来我们来看send⽅法, send__next__()⼀样都可以让⽣成器执⾏到下⼀个yield

    def eat():
        print("我吃什么啊")
        a = yield "馒头"
        print(a)
        b = yield "⼤饼"
        print(b)
        c = yield "⾲菜盒⼦"
        print(c)
        print("程序结束")
        yield "GAME OVER"
    gen = eat() # 获取⽣成器
    ret1 = gen.__next__()
    print(ret1)
    ret2 = gen.send("胡辣汤")
    print(ret2)
    ret3 = gen.send("狗粮")
    print(ret3)
    ret4 = gen.send("猫粮")
    print(ret4)
    

      send__next__()区别:
      1. sendnext()都是让⽣成器向下走⼀次
      2. send可以给上⼀个yield的位置传递值, 不能给最后⼀个yield发送值. 在第⼀次执⾏⽣
      成器代码的时候不能使⽤send()



     

     

  • 相关阅读:
    微服务架构总结
    微服务-网关服务
    HttpClient-RestTemplate-Feign
    RPC和REST
    Springmvc的拦截器执行顺序及各方法作用
    秒杀系统优化方案(下)吐血整理
    秒杀系统优化方案(上)吐血整理
    分布式session的管理
    缓存设计——缓存和数据库的数据一致性
    个人理解的javascript作用域链与闭包
  • 原文地址:https://www.cnblogs.com/liuye1990/p/9197877.html
Copyright © 2011-2022 走看看