Python3打印输出不一致



以下代码:

def fL0(L0=[]):
L0.append(5)
return L0
def fL1(L1=[]):
L1.append(5)
return L1
def fL2(L2=[]):
L2.append(5)
return L2
print("{} {} {}".format(fL0(),fL0(),fL0()))
print(f'{fL1()} {fL1()} {fL1()}')
print(   fL2(),  fL2(),  fL2()  )

正在给出以下输出:

[5, 5, 5] [5, 5, 5] [5, 5, 5]
[5] [5, 5] [5, 5, 5]
[5, 5, 5] [5, 5, 5] [5, 5, 5]

为什么输出[5] [5] [5]没有在这里得到回答。

我的问题是:为什么相同的打印值会有不同的输出哪一个输出是"正确的"?

以下是它在REPL中的样子:

>>> def fL0(L0=[]):
...     L0.append(5)
...     return L0
... 
>>> print("{} {} {}".format(fL0(),fL0(),fL0()))
[5, 5, 5] [5, 5, 5] [5, 5, 5]
>>> print("{} {} {}".format(fL0(),fL0(),fL0()))
[5, 5, 5, 5, 5, 5] [5, 5, 5, 5, 5, 5] [5, 5, 5, 5, 5, 5]
>>> print("{} {} {}".format(fL0(),fL0(),fL0()))
[5, 5, 5, 5, 5, 5, 5, 5, 5] [5, 5, 5, 5, 5, 5, 5, 5, 5] [5, 5, 5, 5, 5, 5, 5, 5, 5]
>>> def fL1(L1=[]):
...     L1.append(5)
...     return L1
... 
>>> print(f'{fL1()} {fL1()} {fL1()}')
[5] [5, 5] [5, 5, 5]
>>> print(f'{fL1()} {fL1()} {fL1()}')
[5, 5, 5, 5] [5, 5, 5, 5, 5] [5, 5, 5, 5, 5, 5]
>>> print(f'{fL1()} {fL1()} {fL1()}')
[5, 5, 5, 5, 5, 5, 5] [5, 5, 5, 5, 5, 5, 5, 5] [5, 5, 5, 5, 5, 5, 5, 5, 5]

这里有一些证据表明,在所有三个版本中,论点都是从左到右评估的:

from time import perf_counter as T
sT=T(); print("{} {} {}".format(T()-sT,T()-sT,T()-sT))
sT=T(); print(f'{T()-sT} {T()-sT} {T()-sT}')
sT=T(); print(   T()-sT,  T()-sT,  T()-sT  )
L = 
[['627274.23131541', '627274.23131578','627274.23131589'], 
['627274.23133362', '627274.23133663','627274.23133841'],
['627274.23134319', '627274.23134330','627274.23134340']]
from pprint import pprint
pprint(list(zip(L[0],L[1],L[2])))
L_T = 
[('627274.23131541', '627274.23133362', '627274.23134319'),
('627274.23131578', '627274.23133663', '627274.23134330'),
('627274.23131589', '627274.23133841', '627274.23134340')]

评估参数的值似乎不会在最终打印中使用,因为评估参数的第一个值不是[5,5,5],而是[5]。但是这个值没有打印出来。。。何时评估参数并不重要。它们必须不同,因为函数在随后的调用中返回不同的值,对不对?如何知道函数在打印输出的哪个调用上返回什么???

这里是另一个没有增长列表的相同行为的例子:

def fn1(L, incr):
L[0] += incr
return L
lst = [0]
print(f'{fn1(lst,2)} {fn1(lst,3)}')
print(f'{fn1(lst,2)} {fn1(lst,3)}')
def fn2(L, incr):
L[0] += incr
return L
lst = [0]
print( fn2(lst,2), fn2(lst,3) )
print( fn2(lst,2), fn2(lst,3) )

输出为:

[2] [5]
[7] [10]
[5] [5]
[10] [10]

似乎f-string是在为输出复制评估结果,而print并没有复制评估结果——只存储对它的引用,以供以后在输出中使用。如果后续评估的参考值更改print()打印错误的值,因为它没有复制之前评估的值。

换句话说,f-string做得很好,并将评估时的当前值复制到输出中,而纯打印不会像预期的那样,因为它没有复制评估结果以在所有评估步骤完成后用于最终输出。此行为是否可以被视为f字符串代码中修复的print()中的错误无论如何,这是不一致的,但应该是。。。

调用函数时,所有用作参数的表达式都会在函数执行之前调用。因此,对fL2()的所有3次调用都发生在调用print之前。

似乎f字符串的求值模式不同,所以每次调用fL1()后都会看到结果。

编辑:根据你对问题的补充,我认为你理解发生了什么,但只是拒绝相信这是正确的行为。在Python中,除非显式地进行复制,否则不会复制任何内容。函数的所有不同迭代总是返回同一个对象,无论它是默认参数还是传入的。可变对象即使在返回后也可能发生变化-重要的是当使用该对象的值时

print以与f-string相同的方式工作很容易,您只需要显式复制每个函数调用的返回值,这样它就不会被下一个函数更新。

>>> print(fL2().copy(), fL2().copy(), fL2().copy())
[5] [5, 5] [5, 5, 5]

最新更新