鱼C论坛

 找回密码
 立即注册
查看: 2187|回复: 3

[技术交流] 分析一个『简单』的Python程序

[复制链接]
发表于 2018-4-2 12:32:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
著名的斐波拉契数列,构造一个生成器类(简单吧。
----------------------------------------------------------------
class Fib(object):

    def __init__(self, limit):
        self.a = 0
        self.b = 1
        self.limit = limit

    def __iter__(self):
        return self       

    def __next__(self):
        self.a, self.b = self.b, self.a+self.b
        if self.a > self.limit:
            raise StopIteration
        return self.a                #!

    def __repr__(self):
        return str(self.a)

m = Fib(60)

for i in m:
    print(m, end=" ")

==================================================

请看代码中我用#!标出的部分。
试分析如果把这里return换成yield会发生什么。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-4-2 12:32:48 | 显示全部楼层
出现了一连串的000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000……
请解释这一现象。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-11-14 02:13:17 | 显示全部楼层
个人理解,重点在于yield的性质:函数中存在yield时,(例如 def f(x): x+=1 ;yield x; )赋值操作a=f(1)只会把a变成生成器,而不执行函数内的任何语句。直到出现类似next(a)的语句时,才会第一次执行函数,并返回yield后面的值。
运用在这里,由于yield的存在,__next__只给i赋值为生成器,需要等待next(i)或send(i)这样的命令,才会执行生成器i内部的迭代命令,进而执行内部的语句。但是实际上没有此命令,所以每次对m的迭代,都执行不了__next__内的语句,只把重复地把 i 变成生成器。无法执行判断,所以停不下来,m的打印也没变。
而用return则可以正常执行语句,也能做判断结束m的迭代,并把赋值给i。
你可以试试在for语句中把i打印出来。看看两种情形下的i有何不同。
不过其实i被赋予了数值还是生成器,其实本身不影响m的迭代。关键在yield只赋值,不迭代,从而无法执行计算和判断语句,使得m无法被赋新的值,也使得迭代无法停止。
我的话语可能表达不够清楚,但重点有两个:1、yield的作用方式比较特殊,第一次赋值时啥也不执行。 2、在用了yield时,m和i都是迭代器(生成器也是特殊的迭代器),只不过i永远得不到迭代命令,而m一直在迭代。
一个不太好看的解决办法是,把      
        self.a, self.b = self.b, self.a+self.b
        if self.a > self.limit:
            raise StopIteration
这一段移到__repr__内。
这样i仍然是一个不被迭代执行的生成器,但m的迭代可以顺利的进行和结束。

同为初学者,我在你这个问题卡了好几个小时,才勉强猜出一个自认为合理的结果,如果能帮到你就再好不过了。如果有问题,也还请指正。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-11-14 12:58:02 | 显示全部楼层
想到一个更好的办法,i不是没有执行迭代吗?那就给他迭代。在for里面加一个 next(i),就行了。
for i in m:
    try:
        next(i)
        print(m, end=" ")
    except:
        break      
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-16 18:17

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表