• 生成器


    生成器

    一、yield关键字

    yield的英文单词意思是生产,在函数中但凡出现yield关键字,在调用函数,就不会继续执行函数体代码,而是会返回一个值。

    def func():
        print(1)
        yield
        print(2)
        yield
    
    
    g = func()
    print(g)
    

    <generator object func at 0x000002509951FE60>

    生成器的本质就是迭代器,同时也不仅仅是迭代器,不过迭代器之外的用途实在是不多,所以我们可以大声的说:生成器提供了非常方便的自定义迭代器的途径。并且从Python2.5+开始,[PEP 342:通过增强生成器实现协同程序]的实现为生成器加入更多的特性,这意味着生成器还可以完成更多的工作,这部分我们会在稍后的部分介绍。

    def func():
        print('from func 1')
        yield 'a'
        print('from func 2')
        yield 'b'
    
    
    g = func()
    print(f'g.__iter__ == g: {g.__iter__() == g}')
    res1 = g.__next__()
    print(f'res1: {res1}')
    res2 = next(g)
    print(f'res2: {res2}')
    

    g.__iter__ == g: True
    from func 1
    res1: a
    from func 2
    res2: b

    1.1 yield+reutrn

    既然生成器函数也是函数,那么它可以使用return输出返回值吗?

    亲,既然你都选择自定义一个函数作为生成器,你还return干什么?如果这是在Python2中,Python解释器会赠送给你一个异常,但是在Python3中,他也不管你这种傻瓜行为了。

    def i_wanna_return():
        yield 'a'
        yield 'b'
        return None
        yield 'c'
    
    
    for i in i_wanna_return():
        print(i)
    

    a
    b

    1.2迭代器套迭代器

    如果我需要在生成器的迭代过程中继而另一个生成器的迭代怎么办?写成下面这样好傻好天真。并且你这样做的意图是什么?

    def sub_generator():
        yield 1
        yield 2
        for i in range(3):
            yield i
    
    
    for i in sub_generator():
        print(i)
    

    1
    2
    0
    1
    2

    def sub_generator():
        yield 1
        yield 2
        yield from range(3)
    
    
    for i in sub_generator():
        print(i)
    

    1
    2
    0
    1
    2

    二、协同程序

    协同程序(协程)一般来说是指这样的函数:

    • 彼此间有不同的局部变量、指令指针,但仍共享全局变量;
    • 可以方便的挂起、恢复,并且有多个入口点和出口点;
    • 多个协程程序间表现为协作运行,如A的运行过程中需要B的结果才能继续执行。

    协程的特点决定了同一时刻只能由一个协程程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁,或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。

    从另一方面说,协程无法并发其实也将它的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多的被拿来与常规函数进行比较,而不是与线程。当然线程比协程复杂许多,功能也更强大,所以我建议大家牢牢的掌握线程即可,是不是听了一脸懵逼,那么就别管他了,因为并发编程你会重新学习他。因此这一节里就不列举关于协程的例子了,以介绍的方法了解即可

    由于Python2.5+对生成器的增强实现了协程的其他特点,在这个版本中,生成器加入了如下方法:

    2.1 send(value):

    send是除了next外另一个恢复生成器的方法。Python2.5+中,yield语句变成了yield表达式,这意味着yield现在可以由一个值,而这个值就是在生成器的send方法被调用从而恢复执行时,调用send方法的参数。

    def h():
        print('--start--')
        first = yield 5  # 等待接受  Fighting!值
        print('1', first)
        second = yield 12  # 等待接受  hahaha!值
        print('2', second)
        yield 13
        print('--end--')
    
    
    g = h()
    first = next(g)  # m获取了yield 5 的参数值5
    # (yield 5)表达式被赋予了'Fighting!', d 获取了yield 12 的参数值 12
    second = g.send('Fighting!')
    third = g.send('hahaha!')
    print('--over--')
    print(f'first:{first},second:{second},third:{third}')
    

    --start--
    1 Fighting!
    2 hahaha!
    --over--
    first:5,second:12,third:13

    • 调用send传入非None值前,生成器必须处于挂起状态,否则将抛出异常。不过未启动的生成器仍可以使用None作为参数调用send
    • 如果使用next恢复生成器,yield表达式将是None。

    2.2close()

    这个方法用于关闭生成器。对关闭生成器后再次调用next或send将抛出Stoplteeation异常。

    def repeater():
        n = 0
        while True:
            n = (yield n)
    
    
    r = repeater()
    r.close()
    print(next(r))  # StopIteration
    

    2.3throw(type,value=None,traceback=None)

    中断Generrator是一个非常灵活的技巧,可以通过throw抛出一个GeneratorExit异常来终止Generator。Close()方法作用是一样的,其实内部它是调用了throw(GeneratorExit)的。我看看close的源码。

    def close(self):
        try:
            self.throw(GeneratorExit)
        except (GeneratorExit, StopIteration):
            pass
        else:
            raise RuntimeError("generator ignored GeneratorExit")  # Other exceptions are not caught
    

    三、自定义range()方法

    def my_range(start, stop, step=1):
        while start < stop:
            yield start
            start += 1
    
    
    g = my_range(0, 3)
    print(f'list(g): {list(g)}')
    

    list(g): [0, 1, 2]

    四、总结

    yield:

    1. 提供一种自定义迭代器的方式
    2. yield可以暂停住函数,并提供当前的返回值

    yield和return:

    1. 相同点:两者都是在函数内部使用,都可以返回值,并且返回值没有类型和个数的限制
    2. 不同点:return只能返回一次值:yield可以返回多次值

    五、生成器表达式

    • 把列表推导式的[]换成()就是生成器表达式
    • 优点:省内存,一次只阐述一个值在内存中
    t = (i for i in range(10))
    print(t)
    print(f'next(t): {next(t)}')
    

    <generator object at 0x000001FF4FCAFE60>
    next(t): 0

    5.1生成器表达式和列表推导式

    泪飙推到式相当于直接给你一筐蛋,而生成器表达式相当于给你一只老母鸡。

    # 生成器表达式
    with open('52.txt', 'r', encoding='utf8') as f:
        nums = [len(line) for line in f]
    
    print(max(nums))
    

    1

    # 列表推导式
    with open('52.txt','r',encoding='utf8') as f:
        nums = (len(line) for line in f)
    
    print(max(nums)) # ValueError: I/O operation on closed file.
    
  • 相关阅读:
    0309. Best Time to Buy and Sell Stock with Cooldown (M)
    0621. Task Scheduler (M)
    0106. Construct Binary Tree from Inorder and Postorder Traversal (M)
    0258. Add Digits (E)
    0154. Find Minimum in Rotated Sorted Array II (H)
    0797. All Paths From Source to Target (M)
    0260. Single Number III (M)
    0072. Edit Distance (H)
    0103. Binary Tree Zigzag Level Order Traversal (M)
    0312. Burst Balloons (H)
  • 原文地址:https://www.cnblogs.com/Lin2396/p/11596252.html
Copyright © 2020-2023  润新知