背景
在联系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[:]
修改后的逻辑: