• python学习日记(生成器函数进阶)


    迭代器和生成器的概念

    迭代器

    对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数。iter()是python内置函数。 
    iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的元素。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。
    
    迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的_next_方法(Python3中是对象的_next_方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现_iter_方法,而_iter_方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的_iter_方法返回自身self即可。
    迭代器定义
    1,迭代器协议:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。
    
    2,可迭代对象:实现了迭代器协议对象。list、tuple、dict都是Iterable(可迭代对象),但不是Iterator(迭代器对象)。但可以使用内建函数iter(),把这些都变成Iterable(可迭代器对象)。
    
    3,for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束
    迭代器的术语解释

    生成器

    作用:
    作用:延迟操作。也就是在需要的时候才产生结果,不是立即产生结果。
    注意事项:
    >生成器是只能遍历一次的。
    >生成器是一类特殊的迭代器。
    分类:
    第一类:生成器函数:还是使用 def 定义函数,但是,使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。
    第二类:生成器表达式:类似于列表推导,只不过是把一对大括号[]变换为一对小括号()。但是,生成器表达式是按需产生一个生成器结果对象,要想拿到每一个元素,就需要循环遍历。
    生成器

    对于生成器来说,看到yield一定要有在这里挂起的概念!!!

    生成器

    send

    一、

    def generator():
        print(123)
        f = yield 'aaa'#3、f用来接收send传来的参数
        print(f)
        print(456)
        yield 'bbb'
    g = generator()
    print(g.__next__())#1、第一次算是激活,然后在yield处挂起
    print(g.send('new day'))#2、从上次的挂起处开始,传递一个参数给yield 返回值

    注意:在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,执行send方法会报错,除非你传的是None。

    二、

    想给最后一个yield使用send,那么最后你还应该再添加一个yield语句防止StopIteration异常。

    def generator():
        print(123)
        f1 = yield 'aaa'#3、f用来接收send传来的参数
        print(f1)
        print(456)
        f2 = yield 'bbb'
        print(f2)
        yield ''#不显示None
    g = generator()
    print(g.__next__())#1、第一次算是激活,然后在yield处挂起
    print(g.send('new day'))#2、从上次的挂起处开始,传递一个参数给yield 返回值
    print(g.send('star lord'))

    练习:获取动态平均值

    实现:依次输入10、20、30,所求的平均值为:10、15、20。

    def average():
        sum = 0
        count = 0
        avg = 0
        while True:
             num = yield avg#num = 10
             count += 1#count = 1
             sum += num#sum = 10
             avg = sum/count#avg = 10/1=10
    a = average()#1、返回迭代器
    a1 = a.__next__()#2、在yield avg 这里挂起,返回的avg = 0
    print(a1)
    a1 = a.send(10)#3、挂起处开始,10传给num,count =0+1=1,sum = 10,avg = 10/1=10,执行直到遇见yield,返回avg,a1 接收 avg,重新挂起
    print(a1)
    a1 = a.send(20)#4、挂起处开始,20传给num,a1 接收 avg,在下一次yield avg重新挂起
    print(a1)
    a1 = a.send(30)
    print(a1)

    激活生成器的装饰器

    def init(f):#f == average
        def inner(*args,**kwargs):
            g = f(*args,**kwargs)#a = average(),返回的是生成器
            next(g)#生成器激活
            return g#再返回给调用inner处
        return inner
    @init
    def average():
        sum = 0
        count = 0
        avg = 0
        while True:
             num = yield avg#num = 10
             count += 1#count = 1
             sum += num#sum = 10
             avg = sum/count#avg = 10/1=10
    avg = average()#avg = inner()
    #省去了激活的步骤,next
    print(avg.send(10))
    print(avg.send(40))

    yield  from 

    一、

    def generator():
        a = '123'
        b = [4,5,6]
        for i in a:
            yield i
        for j in b:
            yield j
    g = generator()
    for k in g:
        print(k,end=' ')

    二、

    def generator():
        a = '123'
        b = [4,5,6]
        yield from a
        yield from b
    g = generator()
    for k in g:
        print(k,end=' ')

     

    生成器的表达式

    列表推导式

    列表解析:

    apple = ['apple%s'%i for i in range(5)]
    print(apple)

    生成器表达式

    把列表解析里面的 [] 换成 () 就变成了生成器表达式。

    little_girl = ('蘑菇%s'%i for i in range(10))
    l = little_girl
    print(l)
    for i in l:
        print(i,end='')

    注:生成器表达式几乎不占用内存,列表解析可能会占用大量内存。

    表达式的筛选

    一、30以内能被3整除的数字的平方。

    l = [i**2 for i in range(31) if i%3 == 0]
    print(l)

    二、生成器同理,只是换了括号。

    嵌套筛选

    找到嵌套列表中名字含有两个‘e’的所有名字。

    names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
             ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
    re = [i for li in names for i in li if i.count('e') == 2]
    print(re)

    其他推导式

    字典推导式

    一、

    将一个字典的key 和value 对调。

    dic = {'libai':'happy','dufu':'sad'}
    d = {dic[k]: k for k in dic}
    print(d)

    二、

    合并大小写对应的value,将key统一成小写。

    dic = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
    d = {k.lower():dic.get(k.lower(),0)+dic.get(k.upper(),0) for k in dic}#字典get方法,不存在,返回0
    print(d)

    集合推导式

    计算列表中数字的平方,并去重。

    li = [1,-1,2,-2,3]
    se = {i**2 for i in li}
    print(se)

    没有元组推导式是因为加了括号就变成了生成器推导式。

    生成器有趣面试题

    一、

    def demo():
        for i in range(4):
            yield i
    
    g=demo()
    
    g1=(i for i in g)
    g2=(i for i in g1)
    
    print(list(g1))
    print(list(g2))

    解析:首先、生成器的特性就是惰性的。当执行print(list(g1))之前,上面的语句虽都定义,但都未执行,执行print(list(g1)),g1作为生成器表达式从生成器g里面遍历取值,然后g1将取到的值拿出生成列表[0,1,2,3]。此时,g1也进行了一次遍历,不再有值了,所以print(list(g2))执行的时候,g2从g1里面取值,取不到值,就生成了一个空列表[]。

    二、

    def add(n,i):
        return n+i
    def test():
        for i in range(4):
            yield i
    g=test()
    for n in [1,10]:
        g=(add(n,i) for i in g)
    print(list(g))

    解析:一步一步化简来看

    def add(n,i):
        return n+i
    def test():
        for i in range(4):
            yield i
    g=test()
    n = 1
    g = (add(n,i) for i in g)
    n = 10
    g = (add(n,i) for i in g)
    print(list(g))

    当n = 1时,并未进行输出,当第二次n = 10之后,才有最后的输出,n进行了两次赋值,后面的进行覆盖,覆盖前后,g都是未执行的(惰性),g前面做了定义,g每一次都是新生成的一个新的生成器,内存地址并不一样,所以需要带入,就有如下:

    def add(n,i):
        return n+i
    def test():
        for i in range(4):
            yield i
    g=test()
    n = 10
    g = (add(n,i) for i in (add(n,i) for i in g))
    # g = (add(10,i) for i in (add(10,i) for i in g))
    # g = (add(10,i) for i in (add(10,i) for i in test()))
    print(list(g))

    如果不太能理解,请看如下:

    b = 10
    b = 1
    #赋值覆盖之前,下面的带入并未执行,模拟生成器的惰性
    a = b
    a = b+a
    a = b+a+a
    print(a)

    ————————————————————————————————————————————

    def add(n,i):
        return n+i
    def test():
        for i in range(4):
            yield i
    g=test()
    print(g)
    n = 1
    g = (add(n,i) for i in g)
    print(g)
    n = 10
    g = (add(n,i) for i in g)
    print(g)
    print(list(g))

    pass

    作者:Gruffalo
    ---------------------------------------------
    天行健,君子以自强不息
    地势坤,君子以厚德载物
    内容仅为自己做日常记录,备忘笔记等
    真小白,努力学习ing...一起加油吧!(ง •̀_•́)ง
  • 相关阅读:
    MySQL通过RPM安装
    HDU4279(2012年天津网络赛---数论分析题)
    Redis实战
    顺序环形队列的各种基本运算
    cocos2d_随手篇1_关于ccTouchBegan的调用
    URAL 1019
    Redis实战之Redis + Jedis
    dataGridView控件--未将对象引用设置添加到对象的实例
    创建 router 连通 subnet- 每天5分钟玩转 OpenStack(100)
    配置 L3 agent
  • 原文地址:https://www.cnblogs.com/smallfoot/p/10054148.html
Copyright © 2020-2023  润新知