Python-生成器

生成器generator本质是一个函数,它记住上一次在函数体中的位置,在生成器函数下一次调用,会自动找到该位置,局部变量都保持不变

l = [x * 2 for x in range(10)]  # 列表生成式
g = (x * 2 for x in range(10))
print(l,g)  # l打印的是一个列表,g则是一个generator的内存地址

一次性打印获取generator的所有元素:

for index in g:
    print(index)

逐步获取generator的元素:

print(g.__next__())     # 0
print(g.__next__())     # 2
print(g.__next__())     # 4
print(g.__next__())     # 6
print(g.__next__())     # 8

yield关键字

通常我们做一个打印0-50的数时,会定义一个函数,只要调用这个函数,它就会自定打印0-50的数

def fib(num):
    n = 0
    while n < num:
        print(n)
        n+=1
fib(50)

其实我们只需要改动那么一丢丢,就可以将上面那个函数改变成一个generator

def fib(num):
    n = 0
    while n < num:
        yield n     # 在使用yield关键字时,需在前面先定义一个变量n
        n+=1
g = fib(50)     # 此时的g是一个generator

generator原理:通过某一种特定的算法,在一个特定的条件下,不断向下推算,得出后续的元素。因为generator不必创建list,所以可以大大的节约内存空间。举个栗子:

def fib():
    print("step 1")
    yield 1
    print("step 2")
    yield 2
    print("step 3")
    yield 3
g = fib()
g.__next__()    # 结果:step 1
g.__next__()    # 结果:step 2
g.__next__()    # 结果:step 3

根据结果可以看出,每次执行__next__(),都会打印一句,而遇到yield就直接跳出,并记录位置,再次执行__next__()时,会从记录的那个位置开始往下执行,再次遇到yield时跳出。
此时我们不经会想,如果我们不断的调用__next__(),该如何判断是否已经完毕,如果越界了,是否会报错?

def fib(num):
    n = 0
    while n < num :
        yield n
        n+=1

g = fib(10)
while True:
    print(g.__next__())

执行结果:

image

可以看出当遍历完毕之后,如果在此调用__next__()将会报错,我们是无法获取到遍历的下标的,那么我们该如何规避这个错误呢?对__next__()抛异常处理

def fib(num):
    n = 0
    while n < num :
        yield n
        n+=1

g = fib(10)
while True:
    try:
        print(g.__next__())
    except StopIteration:
        print("已经完毕")
        break

此时将不再报错,当越界的时候,系统会自动捕捉该异常,并且打印你想要输出的信息

send方法

在单线程下实现一个简单的并行效果

import time
def startEat(name):
    print("%s准备开始吃包子了"%name)
    while True:
        b = yield
        print("%s被%s吃了"%(b,name))
        
def startMake():
    laowang = startEat("laowang")
    laowang.__next__()
    for index in range(10):
        time.sleep(1)
        print("已经做好了包子%d号"%index)
        laowang.send("包子%d号"%index)
startMake()

执行结果:

在这里插入图片描述

yield from

yield from后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,也可以是生成器。yield from 是在Python3.3才出现的语法。所以这个特性在Python2中是没有的。下面来对比一下yieldyield from的使用:

yield

def gen(*args):
    for item in args:
        yield item
        
data_1 = [1, 2, 3]
data_2 = ["a", "b", "c"]
new_list = gen(data_1, data_2)
print(list(new_list))

打印结果如下:

[[1, 2, 3], ['a', 'b', 'c']]

yield from

def gen(*args):
    for item in args:
        yield from item
        
data_1 = [1, 2, 3]
data_2 = ["a", "b", "c"]
new_list = gen(data_1, data_2)
print(list(new_list))

打印结果如下:

[1, 2, 3, 'a', 'b', 'c']

对比上面两种方式可以看出,yield from后面加上可迭代对象,它可以把可迭代对象里的每一个元素一个一个yield出来!如果使用yield来实现这种效果,则需要多加一层遍历!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值