python 遇坑系列一 生成器generator中的坑

背景

在联系pyhon基础时候,遇到一个百思不解的问题,于是扒拉了一下。
廖雪峰官网python 高级特性-生成器 章节
在课后联系 杨辉三角形过程中遇到的坑

先看现象

运行截图
怎么和自己预期的不一样呢?难道自己抄的杨辉三角形定义都能出错? 于是我抱着怀疑的态度去打印了一下 triangles()
在这里插入图片描述
完全没有问题呀? 怎么上面的运行结果全是最后一行,即第10行的呢?这么诡异!?(其实看到结果我心里已经预感到出问题的地方了,只是想去理清一下其中的逻辑,顺便加深一下印象,后面才能避免类似的坑)

扒它的衣服看里面

其实我当我看到结果的时候,我就猜到多半是定义变量时候,变量指向的问题。为了看清楚并解决疑惑,必须得找到python中好用的两个工具函数:判断两个变量是否指向同一个对象 id() 和 a is b

  • 好的扒衣服开始:
    先把代码放到pycharm中,方便进行调试
def triangles():
    ret = [1]
    while True:
        yield ret
        for i in range(1, len(ret)):
            ret[i] = pre[i] + pre[i - 1]
        ret.append(1)
        pre = ret[:]

# 期待输出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
    results.append(t)
    n = n + 1
    if n == 10:
        break

for t in results:
    print(t)

if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')

打上断点,前面走几步好看看它走路的曼妙身姿,前面的每一步我都要去细品一番
在这里插入图片描述
开始调试跟踪,这个过程比较多,我就不一一截图细说,毕竟有些东西要自己去细品才有味道,我这里上几个关键的点进行分析
通过debug 配合 f9 进行单步调试即可,调试过程中看关键变量的变化

外层的关键变量: results , t
triangles中的关键变量:ret , pre

当循环跟到第二圈时候:
results[[1,1]] t[1,1]
ret[1,1] pre[1,1]
在这里插入图片描述
triangles内部

id(ret)
2355532678728
id(ret[0])
140735827321232
id(ret[1])
140735827321232


id(pre)
2355532554568
id(pre[0])
140735827321232
id(pre[1])
140735827321232

外层

id(results)
2355532610376
id(results[0])
2355532678728

id(t)
2355532678728
id(t[0])
140735827321232
id(t[1])
140735827321232

可以看出,t就是 yield ret。 关键点: results中的元素,是可变的,直接指向了ret
比如 results[0] 指向了ret, 而ret每执行一次,就会变成杨辉三角行的下一行。这就导致了results[0] 不在是固定的 1,results[1] 不是固定的[1,1] 进而导致上面的结果。
我再画一下第三遍的结果图

results[[1,3,3,1] ,[1,3,3,1] ,[1,3,3,1]] t[1,3,3,1]
ret[1,3,3,1] pre[1,3,3,1]

在这里插入图片描述

id(results)
2355532610376
id(results[0])
2355532678728
id(results[1])
2355532678728
id(results[2])
2355532678728

id(ret)
2355532678728
id(ret[0])
140735827321232
id(ret[1])
140735827321296
id(ret[2])
140735827321296
id(ret[3])
140735827321232

到这里其实已经扒完了,根本原因就是results 中存入的是变量,改变量指向的是 ret。 ret没执行一遍,就会变成杨辉三角行的下一行。

既然知道了原因,需要怎么修改呢?
只要yield ret 返回的对象不指向ret即可,我们把它换成 ret的复制,类似下面pre 一样。修改办法 yield ret 修改 为 yield ret[:]

yield ret   ->修改为   yield ret[:]

修改后的逻辑:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq1119278087/article/details/108544500