【百尺竿头,更进一步学Python】Python进阶课程——生成器

【百尺竿头,更进一步学Python】Python进阶课程——生成器

学习了迭代器以后,今天我们来讲它的好兄弟生成器.在学习生成器之前,我们先对学过的迭代器进行一次巩固.

迭代器(巩固)

  • 我们都知道利用迭代器,可以在每次迭代获取数据(通过next方法)时,按照特点的规律进行生成,但是我们在实现一个迭代器的时候,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next方法进行迭代使用。这种方式是我们提前将数据创建好了以后才进行的.
  • 在Python中除了for遍历语法在in之后需要使用迭代器,其他的一些列表类型转换(list,tuple)等也需要使用迭代器

生成器

什么是生成器

  • 生成器是一种特殊的迭代器

创建生成器

创建生成器有两种方式:

  1. 创建生成器,只需要把一个列表推导式的[]改成()即可

    p = (i for i in range(1000))
    print(p)
    
  2. 创建生成器,只需要在一个函数中使用yield语句,那么这个函数也不再叫函数,就叫生成器。yield在函数里面出现,这就是一个生成器模板.(我们运用以前学过的案例"水仙花数(完数)"来进行讲解)

    def create_shui(startnum, endnum):
        while startnum < endnum:
            ge = startnum % 10
            shi = (startnum % 100) // 10
            bai = (startnum % 1000) // 100
            qian = startnum // 1000
            if startnum == ge ** 3 + shi ** 3 + bai ** 3 + qian ** 3:
                yield startnum
            startnum += 1
    
    
    if __name__ == "__main__":
        ret = create_shui(1, 1000)
        print(ret)
        for item in ret:
            print(item)
    

总结:

  • 只要在调用一个函数时发现函数中有yield,那么这就不是调用函数,而是创建了一个生成器对象

yield关键字

def create_shui(startnum, endnum):
    print("开始...")
    while startnum < endnum:
        print("...1...")
        yield startnum
        print("...2...")
        startnum += 1
        print("...3...")


if __name__ == "__main__":
    # ret本质是一个生成器对象,生成器的本质是一个迭代器
    ret = create_shui(1, 5)
    res = next(ret)
    print(res)
    # 执行的最后结果是:
    # 开始...
    # ...1...
    # 1
从而我们可以得出yield关键字的本质.

yield的本质

  1. 将该函数标记为生成器,在调用有yield关键字的函数时,目标生成一个生成器对象。对这个生成器对象进行迭代时,每次的迭代都只执行到yield这块。而yield后面的数据是本次迭代的产物数据。只要不进行下一次迭代,代码流程一致监听在yield这块
  2. 保存当前的运行状态,然后暂停执行,也就是将生成器挂起
  3. yield关键字后面的表达式作为值返回

注意:

  • python3中的生成器里面可以使用return返回最终运行的返回值,
  • 而在Python2中生成器里面绝对不允许出现return的返回值(Python2中可以使用return但是return后面绝对不能有数据表达式)

生成器的唤醒

使用next()函数

  • 我们可以对生成器使用next()函数让生成器开始迭代一次,也就是唤醒一次生成器

实例:

def create_shui(startnum, endnum):
    print("开始...")
    while startnum < endnum:
        print("...1...")
        yield startnum
        print("...2...")
        startnum += 1
        print("...3...")


if __name__ == "__main__":
    # ret本质是一个生成器对象,生成器的本质是一个迭代器
    ret = create_shui(1, 5)
    res = next(ret)
    print(res)
    # 再来一次
    res = next(ret)
    print(res)
    # 再来一次
    res = next(ret)
    print(res)
    # 执行到最后的结果是
    # 开始...
    # ...1...
    # 1
    # ...2...
    # ...3...
    # ...1...
    # 2
    # ...2...
    # ...3...
    # ...1...
    # 3

使用send唤醒

  1. send一般不会放到第一次启动生成器,如果非要这样做,那么选择传递一个None
  2. send里面的参数会当做信息传递给yield当做yield的结果,然后通过一个变量可以接收这个结果
  3. send的结果是下一调用yield时,yield后面的值

实例:

def create_shui(startnum, endnum):
    print("开始...")
    while startnum < endnum:
        print("...1...")
        res = yield startnum
        print(res)
        print("...2...")
        startnum += 1
        print("...3...")
    else:
        return "结束..."


if __name__ == "__main__":
    ret = create_shui(1, 5)
    print(next(ret))
    # send一般不会放到第一次启动生成器
    # 如果非要这样做,那么选择传递一个None
    # print(ret.send(None))
    print(ret.send("123"))
    print(ret.send("456"))
    # 最后的执行结果是:
    # 开始...
    # ...1...
    # 1
    # 123
    # ...2...
    # ...3...
    # ...1...
    # 2
    # 456
    # ...2...
    # ...3...
    # ...1...
    # 3

并发执行的模拟

import time
def upload():
    while True:
        time.sleep(0.5)
        print("上传文件")
        yield

def download():
    while True:
        time.sleep(1)
        print("下载文件")
        yield

def main():
    ret1 = upload()
    ret2 = download()
    i = 1
    while i <4:
        next(ret2)
        next(ret1)
        i += 1
  • 这个过程其实就是先让ret2先运行一会,当ret2中遇到了yield,让ret1运行

  • 然后当ret1中遇到了yield再让ret2运行,这样就实现了/ret2/ret1/ret2/ret1/…交替运行,最终实现了所谓的并发

猜你喜欢

转载自blog.csdn.net/XVJINHUA954/article/details/108489388