• python基础15上_迭代器_生成器


    # 迭代器和生成器
    # 迭代器:
    # 双下方法 : 很少直接调用的方法。一般情况下,是通过其他语法触发的
    # 可迭代的 —— 可迭代协议 含有__iter__的方法('__iter__' in dir(数据))
    # 可迭代的一定可以被for循环
    # 迭代器协议: 含有__iter__和__next__方法
    # 迭代器一定可迭代,可迭代的通过调用iter()方法就能得到一个迭代器
    # 迭代器的特点:
        # 很方便使用,且只能取所有的数据取一次
        # 节省内存空间
    
    # 生成器
    # 生成器的本质就是迭代器
    # 生成器的表现形式
        # 生成器函数
        # 生成器表达式
    # 生成器函数:
        #含有yield关键字的函数就是生成器函数
        #特点:
            #调用函数的之后函数不执行,返回一个生成器
            #每次调用next方法的时候会取到一个值
            #直到取完最后一个,在执行next会报错
    #!/usr/bin/env python
    # coding:utf-8
    
    # 迭代器即迭代的工具,那什么是迭代呢?
    # 迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
    # while True:  # 只是单纯地重复,因而不是迭代
    #     print('===>')
    
    l = [1, 2, 3]
    count = 0
    while count < len(l):  # 迭代
        print(l[count])
        count += 1
    
    
    ### for 循环就是基于迭代器协议去工作。
    # a = l.__iter__() # 转为可迭代对象
    # print(a.__next__()) # 使用可迭代对象的next方法
    # print(a.__next__())
    # print(a.__next__())
    
    # for i in l:
    #     print(i)
    
    
    indx = 0
    while indx < len(l):
        print(l[indx])
        indx += 1
    
    '''
    #1、为何要有迭代器?
    对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。
    但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器
    
    #2、什么是可迭代对象?
    可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
    'hello'.__iter__
    (1,2,3).__iter__
    [1,2,3].__iter__
    {'a':1}.__iter__
    {'a','b'}.__iter__
    open('a.txt').__iter__
    
    #3、什么是迭代器对象?
    可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
    而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象
    
    文件类型是迭代器对象
    open('a.txt').__iter__()
    open('a.txt').__next__()
    
    
    #4、注意:
    迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
    '''
    
    dic = {'a': 11, 'b': 22, 'c': 33}
    iter_dic = dic.__iter__()  # 得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
    iter_dic.__iter__() is iter_dic  # True
    # print(iter_dic.__iter__() is iter_dic)  # True
    
    """
    print(iter_dic.__next__())  # 等同于next(iter_dic)
    print(iter_dic.__next__())  # 等同于next(iter_dic)
    print(iter_dic.__next__())  # 等同于next(iter_dic)
    # print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志
    
    # 有了迭代器,我们就可以不依赖索引迭代取值了
    """
    iter_dic = dic.__iter__()
    while 1:
        try:
            k = next(iter_dic)
            print(dic[k])
        except StopIteration:
            break
    
    # 这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看 for 循环
    
    #基于for循环,我们可以完全不再依赖索引去取值了
    dic={'a':111,'b':222,'c':333}
    for k in dic:
        print(dic[k])
    
    '''
    #for循环的工作原理
    #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
    #2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
    #3: 重复过程2,直到捕捉到异常StopIteration,结束循环
    '''
    #!/usr/bin/env python
    # coding:utf-8
    
    # 生成器函数, 函数内部有yield ,保留函数的生成状态
    def test():
        yield 1
        yield 2
        yield 3 # 相当于return,但是却可以多次
    g = test()
    print(g)
    print(g.__next__()) # 1
    print(g.__next__()) # 2
    
    
    ### 三元操作符
    name = 'alex'
    res = 'sb' if name =='alex' else 'kkk'
    print(res)
    
    ### 列表解析l
    # egg_list=[]
    # for i in range(10):
    #     egg_list.append('鸡蛋%s' %i)
    # print(egg_list)
    
    lis = ['鸡蛋%s' %i for i in range(10)] # 等同于上面的过程。
    lis2 = ['鸡蛋%s' %i for i in range(20) if i >10]
    lis3 = ['鸡蛋%s' %i for i in range(10) if i > 5] #
    print(lis)
    print(lis2)
    print(lis3)
    
    
    ## 例:找到嵌套列表中名字含有两个'e'的所有名字
    names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
             ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
    
    list3 = [name  for lst in names  for name in lst  if name.count('e') >= 2]
    print(list3)
    
    
    ## 生成器表达式
    ## 优点:省内存,一次只产生一个值在内存中 因为生成器表达式是基于迭代器 __next__ 方法取值 .
    ## 把列表推导式的 [] 换成 () 就是生成器表达式
    lise = ('鸡蛋%s' %i for i in range(10))
    print(lise)
    print(lise.__next__())
    print(lise.__next__())
    print(next(lise))  # next 就是调用了内置的 __next__
    
    # sum 里面可以直接写生成器表达式,因为就是调用了可迭代对象的__next__
    print(sum(i for i in range(10000001))) # 一千万
    #!/usr/bin/env python
    # coding:utf-8
    
    import time
    
    def test():
        print('start 1st...')
        yield 'first generation.' # 遇到yield就返回结果,并保留运行状态。
        time.sleep(3)
        print('start 2nd___')
        yield  'second generation..'
        time.sleep(3)
        print('start 3rd')
        yield  'third generation...'
    
    a = test()
    print(a.__next__()) # 遇到yield就返回结果,并保留运行状态。
    print(a.__next__()) # 继续上次的运行状态往下执行。
    print(a.__next__())
    
    
    """
    1. 迭代器协议: 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就StopIteration异常,以终止迭代
    2. 可迭代对象: 实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
    3. 生成器就是自动实现了迭代器协议. 
    
    """

    假设有个字典格式的文件people 内容如下:

    {'name':'上海','population':999}
    {'name':'杭州','population':8888}
    {'name':'苏州','population':7767}
    {'name':'无锡','population':23456}

    #!/usr/bin/env python
    # coding:utf-8
    
    ### 把函数做成生成器的好处是,next一次取一个值,立刻就可以操作.不必等到下一个值产生.
    ### 得到一个值立马可以处理, next中可以加一些操作.
    def baozi():
        for i in range(100):
            print("正在生产包子...")
            yield '一笼包子%s' %i
            print('收钱.')
    
    a = baozi()
    bz1=a.__next__()
    print(bz1)
    bz2=a.__next__()
    print(bz2)
    
    
    ### 母鸡下蛋的传说:
    def xiadan():
        for i in range(100):
            yield '鸡蛋%s' %i
    
    alex = xiadan()
    print('路人甲获得%s' %alex.__next__())
    print('路人乙获得%s' %alex.__next__())
    
    ## 生成器也可以被 for 循环
    # for i in alex:
    #     print(i)
    
    
    #  import this # 保证代码可读性的前提下,尽量精简
    
    
    ## 取各地区人口的示例:
    def get_population():
        with open('people','r',encoding='utf-8') as f:
            for i in f:
                yield eval(i)
    
    g = get_population() # 得到生成器
    all_pop = sum(i['population'] for i in g) ## 求和总人口
    print(all_pop)
    
    ## 如果使用next来取得列表, 也就没有必要了.没有发挥生成器的优势.

    一个生动的例子: 生产者消费者模型:

    #!/usr/bin/env python
    # coding:utf-8
    
    import time
    
    # def producer():
    #     ret=[]
    #     for i in range(100):
    #         time.sleep(0.01)
    #         ret.append('包子%s' %i)
    #
    #     return ret
    #
    # def consumer(res):
    #     for idx, things in enumerate(res):
    #         time.sleep(0.1)
    #         print('第%s个人,吃了%s' %(idx,things))
    #
    #
    # res = producer()
    # consumer(res)
    
    
    #### 生产者 消费者模型
    #### 使用生成器改写上面的程序,实现并发:
    
    def consumer(name):
        print('我是%s, 我要开始吃包子了.' % name)
        while True:
            baozi=yield
            time.sleep(0.5)
            print('%s 很开心地把 %s 吃掉了.' %(name,baozi))
    
    def producer():
        c1 = consumer('Tom') # 调用上面的生成器函数
        c1.__next__()
        for i in range(10):
            time.sleep(0.5)
            c1.send('肉包子%s' %i) # 由send
    
    producer()
    ## 单线程并发
  • 相关阅读:
    好玩的原生js的简单拖拽
    原生js的简单倒计时
    五分钟了解node,cnpm和yarn
    计算水仙花数
    首师大附中科创教育平台 我的刷题记录(1)
    [暑假集训--数位dp]hdu3652 B-number
    [暑假集训--数位dp]hdu2089 不要62
    cf711E ZS and The Birthday Paradox
    Spoj-NETADMIN Smart Network Administrator
    cf449C Jzzhu and Apples
  • 原文地址:https://www.cnblogs.com/frx9527/p/python_15.html
Copyright © 2020-2023  润新知