python之生成器

生成器对象

生成器对象其实本质还是迭代器,只不过这个迭代器的内容可以由我们直接来定义了,所以它也可以称为自定义迭代器。

image

先来看一段代码:

def index():
    print('abc')
    yield
print(index())  # 输出:<generator object index at 0x000001DEEAF00200>

可以看到,在加了关键字yield之后,函数变成了生成器对象了。如果想要执行函数里的代码,就需要使用__next__()方法。

def index():
    print('abc')
    yield
res = index()
res.__next__()  # 输出:abc

多个yield

def index():
    print('abc')
    yield
    print('def')
    yield
    print('gh')
    yield
res = index()
res.__next__()  # 输出:abc
res.__next__()  # 输出:def
res.__next__()  # 输出:gh

在yield后也可以跟上一个值

def index():
    print('abc')
    yield 123
    print('def')
    yield 456
res = index()
r1 = res.__next__()  # 输出:abc
print(r)  # 输出:123
r2 = res.__next__()  # 输出:def
print(r)  # 输出:456

结论:

  1. 在函数内部添加了yield关键字之后,函数就会变成生成器对象,并且可以调用__next__方法。
  2. 当有多个yield关键字时,每执行一次__next__方法时函数代码会运行到下个yield处并停留。
  3. 如果yield后跟上了一个值,则执行__next__方法时会返回yield后的值。

补充:yield关键字的作用

除了以上的作用外,yield还可以接收外界的传值

def eat(name):
    print(f'{name}准备干饭')
    while True:
        food = yield
        print(f'{name}正在吃{food}')
res = eat('jason')
# 使用send方法前需要调用一次__next__启动
res.__next__()  # 输出:jason准备干饭
# 使用send方法给yield传值
res.send('kfc')  # 输出:jason正在吃kfc
res.send('米饭')  # 输出:jason正在吃米饭

自定义range方法

在了解完生成器对象后,这时候我们就可以自己定义一个和range一样功能的方法了。

先实现两个参数的情况:

def my_range(start, end):
    """
    start: 起步位置
    end: 结束位置
    """
    # 如果start一直累加后值等于end则退出循环
    while start < end:
        yield start
        # 每被执行一次__next__方法起始位置加1
        start += 1
for i in my_range(1, 4):
    print(i)

实现了两个参数后,我们可以开始解决一个参数的情况

def my_range(start, end=None):
    """
    start: 起步位置
    end: 结束位置,默认为空值
    """
    # 如果没有给end传值,说明只有一个参数,取值范围为[0,start)
    if not end:  
        end = start
        start = 0
    # 如果start一直累加后值等于end则退出循环
    while start < end:
        yield start
        # 每被执行一次__next__方法起始位置加1
        start += 1
for i in my_range(1, 4):
    print(i)

最后解决三个参数的情况

def my_range(start, end=None, step=1):
    """
    start: 起步位置
    end: 结束位置,默认为空值
    step:步长,默认为1
    """
    # 如果没有给end传值,说明只有一个参数,取值范围为[0,start)
    if not end:  
        end = start
        start = 0
    # 如果start一直累加后值等于end则退出循环
    while start < end:
        yield start
        # 每被执行一次__next__方法起始位置加步长
        start += step
for i in my_range(1, 4):
    print(i)

image

生成器表达式

直入主题,生成器结构

res = (i for i in 'jason')
print(res)  # 输出:<generator object <genexpr> at 0x000002AC49CF0200>
"""可以看到,它并不会像列表生成式一样直接输出"""
"""因为生成器内部的代码只有在调用__next__迭代取值的时候才会执行"""
print(res.__next__())  # 输出:j
print(res.__next__())  # 输出:a
posted @ 2022-06-06 19:48  Yume_Minami  阅读(101)  评论(0编辑  收藏  举报