Discuss / Python / 这样输出两边会多0,是为什么,我的0不应该是在输出以后加的吗

这样输出两边会多0,是为什么,我的0不应该是在输出以后加的吗

Topic source

轻涉世

#1 Created at ... [Delete] [Delete and Lock User]

def triangles2():

    L = [1]

    yield L

    while True:

        L.insert(0, 0)

        L.append(0)

        L = [L[i] + L[i+1] for i in range(len(L) - 1)]

        L = L

        yield L

Dimcirui

#2 Created at ... [Delete] [Delete and Lock User]

主要原因在于insert和append的原理上。insert和append在添加的时候不会改变列表的内存地址,而+方法会同时改变列表的内存地址(也就是说实际上换了一个列表)。

在某一次循环正常输出后(假设输出[1,1]吧),存到result里后,进入下一次循环——关键点来了:由于insert和append不会改变列表内存地址,因此在这次循环中,虽然你预想中会创造一个新L“[0,1,1,0]”用于计算,但是,由于存在result里的L地址不变,所以事实上是改了前一个已经存好的L“[1,1]”为“[0,1,1,0]”。因为输出并不是在yield以后立即输出的,而是整体弄完后再输出的,所以最后会变成[0,1,1,0]而非[0,0]。

至于为什么两边不会有多个0,那是因为,在之后运行+方法(也就是那个L = L[i]+L[i+1]...)的时候,L的地址变了,再用insert和append不会再影响最初的[0,1,1,0]了。

最终结果就变成了:你每执行一次循环,就会给上一次循环得出的那个列表两边多加两个0。

所以你会发现无论杨辉三角是几层,最后都会有一样的结果:总是最后一个两边没有0。

如果把insert和append改为L = [0] + L + [0],问题就解决了。

根本来说,主要原因在于python保存变量的原理上,python的变量默认存的是地址而不是值,所以有”在python中没有赋值,只有引用“一说。

如果挑一个类似的简单例子的话:直接赋值产生新列表,如果你改变新列表,原列表的值也会变。

@Dimcirui 麻烦请问什么是整体弄完再输出,就这一次while里的语句执行完?

 可,不是每次执行到yield 就停了吗?

多谢

Dimcirui

#4 Created at ... [Delete] [Delete and Lock User]

@INDifferenceeeee 

整体弄完是指下面的部分,不是在说triangles()。你说的执行到yield 就停没有问题,问题是函数本身的设计和下面的部分不太匹配:

n = 0
results = []
for t in triangles():
    results.append(t)
    n = n + 1
    if n == 10:
        break

for t in results:
    print(t)

for t in triangles()部分仅执行append循环,并没有print。是执行完所有append后才print。

也就是说是先保存、再修改(保存的部分),再保存、再修改......依次循环下去,到最后才输出。

但是,如果把print直接放到上面的while部分内,代替掉yield,就不会出现两边有[0]的情形了,因为是先输出后修改的(尽管这样就不是生成器了)。


  • 1

Reply