函数进阶——yield关键字及生成器

本文探讨了Python中的生成器和yield关键字,详细解释了它们如何工作,包括生成器的应用实例、yield的传值功能、与return的区别、生成器表达式以及相关的内置函数。通过实例展示了生成器如何实现range功能,以及使用send方法传递值。文章还讨论了生成器表达式和一些常见的内置函数在生成器中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引出:迭代取值和索引取值的对比

​ 迭代取值提供了一种不依赖于索引取值的新的方式。

  • 迭代取值:

    1. 不依赖索引取值,通过调用__next__()方法取值。
    2. 不能重复取值,只能从左往右挨个取值。若要取同一个值,只能重新初始化迭代器对象,再调用__next__()方法。
  • 索引取值:

    1. 取值的对象必须是一个容器类型,例如列表、元组等
    2. 可以重复取值,即一个值可以取多次,随时取。

生成器与yield关键字

​ 生成器就是自定义迭代器,本质上就是迭代器。如果函数体内包含yield关键字,那么这个函数在调用的时候并不会执行函数体代码,而返回的结果是一个生成器对象。生成器内置有__iter__()方法和__next__()方法,通过生成器对象调用__next__()方法,就会触发函数体代码的执行。

​ yield关键字可以用于返回值,但不同于return关键字,函数遇到return就直接结束,而yield可以保持函数运行状态“暂停”函数,用来返回多次值。

代码演示:

def index():
    print('from index')
    yield
    
index()
print(index())
index().__next__()
print(index().__next__())

执行结果:

image-20220113202140973

结论:函数体包含yield关键字,调用时不执行代码,返回结果为生成器对象;生成器对象调用__next__()方法,执行函数体,并将yield后面的值返回,若不写,默认返回None。

def index():
    print('from index')
    yield 123,234,345
    
res = index()
print(res.__next__())
# print(next(res))  # 与上一句代码作用一致

执行结果:

image-20220113202840862

总结:yield后面可以返回值,并且可以同时返回多个,以元组形式返回。

def index():
    print('第一次next')
    yield 123,234,345
    print('第二次next')
    yield 'aaaa'
    print('第三次next')
    yield 'xxxx'

res=index()  # 一旦是生成器,就可以__next__取值了
print(res.__next__())
print(res.__next__())
print(res.__next__()) 
print(next(res))  # StopIteration

image-20220113203729695

总结:生成器对象调用一次__next__()方法,走一个yield,光标就停在了yield,如果再调用__next__(),会从上一次停留的yield位置继续往下走,如果后面有,则执行,如果没有,则抛出异常。

注意:函数里面有yield关键字之后,函数调用之前还是函数,一旦调用函数变成了生成器

生成器应用实例:生成器实现range功能
  • range功能:

    1. 功能一:
    range(10)  # 0-9的数字
    2. 功能二:
    range(3,10)  # 3-9的数字
    3. 功能三:
    range(1, 10, 2) # 1,3,5,7,9
    
  • 利用生成器实现range功能:

    def my_range(start, end=None, step=1):
        if not end:
            end = start
            start = 0
    
        while start < end:
            yield start
            start += step
    
    # res = my_range(2, 10, 2)
    res = my_range(10)
    
    for i in res:
        print(i)
    
yield传值

​ 有了yield关键字,可以拿到函数的生成器对象持续为函数体send值。

示例:

def index(name):
    print('%s 准备干饭' % name)
    while True:
        food = yield
        print('%s 开干 %s' % (name, food))

res=index('靓仔')
res.__next__()
res.__next__()

res.send('手扒饭')
res.send('盖浇饭')

执行结果:

image-20220113205146994

yield和return的对比
  • yield:

    1. 函数内遇到yield关键字,在调用时把函数变成了生成器
    2. yield 也可以返回值,并且支持返回多个(元组形式)
    3. 遇到yield,函数不会结束,而是"暂停"
    4. yield 也支持传值(利用send方法)
  • return:

    1. return后面可以跟返回值,也支持返回多个(元组形式)
    2. 函数遇到return,会立即结束
生成器表达式

​ 我们从列表生成式,来引出生成表达式,如下代码演示:

  • 列表生成式:

    l = [1,2,3,4]
    new_l = [i + 1 for i in l]
    print(new_l)
    

    执行结果:[2, 3, 4, 5]

    总结:列表生成式的返回值是列表

  • 生成器表达式:

    l = [1,2,3,4]
    new_l = (i + 1 for i in l)
    print(new_l)
    print(next(new_l))
    

    执行结果:

    image-20220113210144111

    总结:生成器表达式返回一个生成器对象,若要取值,需要通过next()来产生,生成器表达式就类似于一个“工厂”,当有需求时,"工厂"才会为你加工“产品”。

生成器笔试题

题目:写出下列程序的执行结果。

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)
    
res = list(g)
print(res)

执行结果:[20, 21, 22, 23]

过程分析:

# 求和
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)
    '''
        第一次循环:
            g = (add(1, i) for i in g)
        第二次循环:
            g = (add(10, i) for i in (add(10, i) for i in g))
    '''
res = list(g)
print(res)
常见内置函数补充

abs():求绝对值。

print(abs(123))
print(abs(-123))

# 执行结果:123 123

all():只有列表中元素全为真,才返回True,否则返回False。

l1 = [1,2,3,4,5,6]
l2 = [1,2,3,0]
print(all(l1))
print(all(l2))

# 执行结果:True  False

any():只要列表中元素有一个为真,就返回True。

l1 = [1,2,3,4,5,6]
l2 = [1,2,3,0]
l3 = [0, None,[]]
print(any(l1))
print(any(l2))
print(any(l3))

# 执行结果:True		True	False

sum():求和,注意传入的对象必须是可迭代对象。

l = [1,2,3,4,5,6]
print(sum(l))

# 执行结果:21

divmod():除余,第一个参数为被除数,第二个参数为除数,返回结果为(商值,余数)。

print(divmod(100,10))
print(divmod(101,10))
print(divmod(99,10))

# 执行结果:(10, 0)	(10, 1)	(9, 9)

应用:分页器的设计。

eval()exec():使字符串中的代码执行,注意字符串中的代码不能缩进,否则报错。

string1 = """
print("hello world")
"""

eval(string1)
eval(string1)

# 执行结果:
hello world
hello world

isinstance():判断某个数据是否为某个实例。

s = "hello world"
print(isinstance(s, int))
print(isinstance(s, str))

# 执行结果:
False
True

chr():传入十进制数,返回十进制数对应的ASCII码字符。

print(chr(65))

# 执行结果:
A

ord():传入ASCII码字符,返回对应的十进制数字。

print(ord('A'))

# 执行结果:
65

pow():参数一为底数,参数二位幂次,返回其结果。

print(pow(2,3))
print(pow(3,3))

# 执行结果:
8
27

round():小数点后到整数五舍六入。

print(round(4.3))
print(round(4.5))
print(round(4.8))

# 执行结果:
4
4
5

bytes():转二进制。

s = 'hello world'
print(bytes(s, 'utf8'))

# 执行结果:
b'hello world'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值