【Python】yield语句使用方法 and 生成器、迭代器的含义

本篇文章将会介绍有关Python中的迭代器、迭代对象、生成器和yield关键字有关知识

首先应该明白几个问题/概念:

1.啥是迭代(器)?

使用一个循环来遍历某个东西时,这个过程本身就叫迭代。一个for循环、一个while循环或者递归函数都属于迭代

那么迭代器就是用来迭代的器???我的理解是,迭代器相当于“指哪打哪”的那根手指头,通俗来讲,迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器(container,例如链表或阵列)上遍访的接口,设计人员无需关心容器的内容。(百度百科)

你要通过下标来遍历一个数组,下标可以简单地看作一个迭代器。

2.啥是生成器?

有了迭代器的大致概念,生成器就好些理解,也是一种迭代器,但只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。

好处显而易见了,节省大量内存空间。我们可以通过遍历来使用它们,要么用一个“for”或“while”循环,要么将它们传递给任意可以进行迭代的函数和结构。

生成器用在哪里呢? 很显然,当你不想在同一时间将所有计算出来的大量结果集分配到内存当中,生成器就显得格外有用。


然后应该明白几个Python的内置函数:

1.内置函数next()

它允许我们获取一个序列的下一个元素。序列这个概念在Python中很常见,比如range()函数所生成的序列。

2.内置函数iter()

它将根据一个可迭代对象返回一个迭代器对象。听起来很拗口,看看下面的例子吧。

3.关键字yield

可以暂时理解为return,看完下面的例子就好体会了。


例1. yield的使用

def test():
    print("starting...")
    for i in range(5):
        print("进入循环...")
        res = yield i
        print("res:", res)
        print("循环结束!")

print("调用g = test():")
g = test()  # 没有任何输出
for i in range(7):
    print("第{}次调用print(next(g)):".format(i + 1))
    try:
        print(next(g))
    except Exception as e:
        print("异常类型",e.__class__.__name__)
        print("异常描述",e.__doc__)

运行结果:

调用g = test():
第1次调用print(next(g)):
starting...
进入循环...
0
第2次调用print(next(g)):
res: None
循环结束!
进入循环...
1
第3次调用print(next(g)):
res: None
循环结束!
进入循环...
2
第4次调用print(next(g)):
res: None
循环结束!
进入循环...
3
第5次调用print(next(g)):
res: None
循环结束!
进入循环...
4
第6次调用print(next(g)):
res: None
循环结束!
异常类型 StopIteration
异常描述 Signal the end from iterator.__next__().
第7次调用print(next(g)):
异常类型 StopIteration
异常描述 Signal the end from iterator.__next__().

执行流程:
这个函数加了yield关键字之后,其实不再是一个函数,更像一个对象了,因此g = test()没有任何输出,不再执行这个函数。

在第一次调用test()时,打印了test()函数体中循环之外的语句,然后进入循环体,遇到了yield关键字就停止了,相当于return了当前的数字i

接着在第二次调用test()时,又从上次该函数return后停止的地方继续执行了,此时res变量无法接收任何值,可以认为yield返回到了函数外,对res没有赋值。然后运行,直到第二次循环又遇到yield关键字后返回。

到这里,有一个大致的印象:好像这个test()函数就变成了一个序列或者类似数组的容器,每次访问这个函数时,就从上次结束的地方继续执行,然后yield这个函数里面的值来返回(可以结合yield的中文意思产生,产出来理解)

接着执行第3,4,5次调用,直到第6次调用的时候,第5次yield已经结束,退出了函数体内的循环,于是产生了异常Signal the end from iterator,即已经到了迭代器结尾,没有next了…没法迭代了还能强迫不成?第7次调用也是一样。

over!

例2. 把字符串变成迭代器

my_string = "I like Python"
my_iter = iter(my_string)
for i in range(len(my_string)):
    print(next(my_iter),end="\t")

输出结果:

I	 	l	i	k	e	 	P	y	t	h	o	n

如果使用print(next(my_string)) 那么就会报错:TypeError: 'str' object is not an iterator

说明啥?str类型是一个可迭代对象,而不是一个迭代器对象!这意味着它支持迭代,但我们不能直接对其进行迭代操作。因此就理解了iter()将根据一个可迭代对象返回一个迭代器对象的含义

例3. 我们熟悉的range()函数跟迭代器的关系

print("type(range(5))=",type(range(5)))
my_range=iter(range(5))
print("type(my_range)=",type(my_range))
for i in range(5):
    print(next(my_range))

输出结果:

type(range(5))= <class 'range'>
type(my_range)= <class 'range_iterator'>
0
1
2
3
4

原来,序列(range类)类似于一个列表,但也可以变成迭代器(range_iterato类)来使用!

例4. 利用yield生成斐波那契数列

def func_fibon(n):
    a = 1
    b = 1
    for i in range(n):
        yield a
        temp = b
        b = a + b
        a = temp

for x in func_fibon(10):
    print(x,end="\t")

输出结果:

1	1	2	3	5	8	13	21	34	55

文章参考:python中yield的用法详解——最简单,最清晰的解释

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值