zoukankan      html  css  js  c++  java
  • 11 Day Python之迭代器&生成器

    迭代器&生成器

    一、可迭代的&迭代器

    可迭代协议

    ①协议内容:内部实现了__iter__方法

    ②验证方法:dir()方法;使用dir()方法查看数据类型中的方法是否包含__iter__

    ③__iter__()方法的作用:可迭代的数据类型执行__iter__()方法后会生成一个迭代器对象

    ④例子:

    print([1,2].__iter__())
    # 结果
    <list_iterator object at 0x1024784a8>
    """
    dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,
    都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,然后取差集。
    """
    #print(dir([1,2].__iter__()))
    #print(dir([1,2]))
    print(set(dir([1,2].__iter__()))-set(dir([1,2])))
    
    结果:
    {'__length_hint__', '__next__', '__setstate__'}
    
    # 迭代器中多出的三个方法介绍
    iter_l = [1,2,3,4,5,6].__iter__()
    
    #获取迭代器中元素的长度
    print(iter_l.__length_hint__())
    
    #根据索引值指定从哪里开始迭代
    print('*',iter_l.__setstate__(4))
    #一个一个的取值
    print('**',iter_l.__next__())
    print('***',iter_l.__next__())

    二、生成器

    ①种类:python中提供的生成器有:

    • 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
    • 生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

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

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

    ④生成器函数:一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。

    • 1) 生成器函数例子:
    import time
        def genrator_fun1():
            a = 1
            print('现在定义了a变量')
            yield a
            b = 2
            print('现在又定义了b变量')
            yield b
    
        g1 = genrator_fun1()
        print('g1 : ',g1)       #打印g1可以发现g1就是一个生成器
        print('-'*20)   #我是华丽的分割线
        print(next(g1))
        time.sleep(1)   #sleep一秒看清执行过程
        print(next(g1))
    • 2) 生成器函数的好处:可以避免一次性读取数据到内存中导致内存溢出
    # 例子01:
    def produce():
        """生产衣服"""
        for i in range(2000000):
            yield "生产了第%s件衣服"%i
    
    product_g = produce()
    print(product_g.__next__()) #要一件衣服
    print(product_g.__next__()) #再要一件衣服
    print(product_g.__next__()) #再要一件衣服
    num = 0
    for i in product_g:         #要一批衣服,比如5件
        print(i)
        num +=1
        if num == 5:
            break
    
    #到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。
    #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
    
    # 例子02:
    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')
    for line in tail_g:
        print(line)
    # 例子03:
    def averager():
        total = 0.0
        count = 0
        average = None
        while True:
            term = yield average
            total += term
            count += 1
            average = total/count
    
    g_avg = averager()
    next(g_avg)
    print(g_avg.send(10))
    print(g_avg.send(30))
    print(g_avg.send(5))
    # 例子04:
    def init(func):  #在调用被装饰生成器函数的时候首先用next激活生成器
        def inner(*args,**kwargs):
            g = func(*args,**kwargs)
            next(g)
            return g
        return inner
    
    @init
    def averager():
        total = 0.0
        count = 0
        average = None
        while True:
            term = yield average
            total += term
            count += 1
            average = total/count
    
    g_avg = averager()
    # next(g_avg)   在装饰器中执行了next方法
    print(g_avg.send(10))
    print(g_avg.send(30))
    print(g_avg.send(5))
    • 3) yield from
    def gen1():
        for c in 'AB':
            yield c
        for i in range(3):
            yield i
    print(list(gen1()))
    
    以上是将生成器中的数据以列表的方式打印
    以下的执行效果与上面相同,运用了yield from 语法
    def gen2():
        yield from 'AB'
        yield from range(3)
    
    print(list(gen2()))

    ⑤生成器函数进阶

    send():

    1. send和next的作用相同
    2. 第一次不能用send
    3. 函数中的最后一个yield不能接受新的值
    # 计算移动平均值的例子
    def average():
        sum = 0
        count = 0
        avg = 0
        while True:
            num = yield avg
            sum += num
            count += 1
            avg = sum / count
    
    avg_g = average()
    avg_g.__next__()
    
    print(avg_g.send(20))
    print(avg_g.send(40))
    print(avg_g.send(60))
    # 预激生成器的装饰器——在装饰器中首先调用了__next__方法,方便用户直接进行调用
    def init(func):
        def wrapper(*args,**kwargs):
            g = func(*args,**kwargs)
            g.__next__()
            return g
        return wrapper
    
    @init
    def average():
        sum,avg,count = 0,0,0
        while True:
            num = yield avg
            sum += num
            count += 1
            avg = sum/count
    
    avg_g = average()
    print(avg_g.send(10))
    print(avg_g.send(20))
    print(avg_g.send(30))

    ⑥生成器表达式

    '''
    列表推导(列表生成式)
    '''
    
    # 简单的列表推导
    l = [i ** 2 for i in range(10)]
    print(l)
    
    '''
    生成器表达式
    与列表推导相似,返回生成器对象
    '''
    
    g = (i ** 2 for i in range(10))
    
    # 生成器的第一种调用方式:__next__
    print(g.__next__())
    
    # 生成器的第二种调用方式:for循环
    for i in g:
        print(i)
    
    # 生成器的第三种调用方式:类型强转
    print(list(g))

    ⑦各种推导式

    '''
    列表推导
    [每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型]    #遍历之后挨个处理
    [满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件]   #筛选功能
    '''
    # 简单的列表推导
    l = [i ** 3 for i in range(10)]
    print(l)
    >>> [0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
    
    # 带筛选的列表推导
    l = [i ** 3 for i in range(10) if i % 3 == 0]
    print(l)
    >>> [0, 27, 216, 729]
    
    # 多层列表推导
    double_l = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
                ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
    l = [name for lst in double_l for name in lst if name.count('e') >= 2]
    print(l)
    >>> ['Jefferson', 'Wesley', 'Steven', 'Jennifer']
    
    '''
    字典推导
    '''
    # 例一:将一个字典的key和value对调
    mcase = {'a': 10, 'b': 34}
    mcase_frequency = {mcase[k]: k for k in mcase}
    print(mcase_frequency)
    >>> {34: 'b', 10: 'a'}
    
    # 例二:合并大小写对应的value值,将k统一成小写
    mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
    mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
    print(mcase_frequency)
    >>> {'b': 34, 'a': 17, 'z': 3}
    
    '''
    集合推导
    '''
    # 计算列表中每个值的平方,自带去重功能
    squared = {x ** 2 for x in [1, -1, 2]}
    print(squared)
    >>> {1, 4}
    
    '''
    练习题
    '''
    
    # 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
    lst = ['123', 'ab', 'y', 'abcabc', 'okiuj']
    l = [i.upper() for i in lst if len(i) > 3]
    print(l)
    >>> ['ABCABC', 'OKIUJ']
    
    # 答案
    # [name.upper() for name in names if len(name)>3]
    
    # 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表
    lst = [(4, 3), (3, 2), (8, 1), (8, 6)]
    t = [i for i in lst if ((i[0] % 2 == 0) and (i[1] % 2 != 0))]
    print(t)
    >>> [(4, 3), (8, 1)]
    
    # 答案
    # [(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1]
    
    # 求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]]
    M = [[1,2,3],[4,5,6],[7,8,9]]
    l = [i[2] for i in M]
    print(l)
    >>> [3, 6, 9]
    
    # 答案
    # [row[2] for row in M]
  • 相关阅读:
    Codeforces 691A Fashion in Berland
    HDU 5741 Helter Skelter
    HDU 5735 Born Slippy
    HDU 5739 Fantasia
    HDU 5738 Eureka
    HDU 5734 Acperience
    HDU 5742 It's All In The Mind
    POJ Euro Efficiency 1252
    AtCoder Beginner Contest 067 C
    AtCoder Beginner Contest 067 D
  • 原文地址:https://www.cnblogs.com/lidaxu/p/8195194.html
Copyright © 2011-2022 走看看