Following code:
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() )
is giving following output:
[5, 5, 5] [5, 5, 5] [5, 5, 5]
[5] [5, 5] [5, 5, 5]
[5, 5, 5] [5, 5, 5] [5, 5, 5]
The question why isn't the output [5] [5] [5]
is answered here .
My question is: How does it come that there is different output for the same printed values? And which of the outputs is the 'right one'?
Here how it looks like in 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]
And here some evidence that in all three versions the arguments are evaluated from left to the right:
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')]
The value of the evaluated parameter seems not to be used in the final printing, as the first of the evaluated parameter does NOT evaluate to [5, 5, 5] but [5]. But this value is not printed ... It does not matter when the parameter are evaluated. They have to be different as the function returns different values in subsequent calls, right or not right? How to know what the function returns on which call from the printed output???
Here another example of same behavior without growing list:
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) )
The output is:
[2] [5]
[7] [10]
[5] [5]
[10] [10]
It seems that f-string is copying the evaluated result for the output and print doesn't copy the evaluated result storing only the reference to it for later use in the output. If the value of the reference change with subsequent evaluation print()
prints the wrong value as it didn't copy the one evaluated before.
In other words f-string does it right and copies the current value at the time of evaluation to the output and pure print does it not as expected as it does not copy the result of the evaluation for use in final output after all evaluation steps are finished.
Can this behavior be considered a bug in print()
fixed in the code of f-strings? Anyway, it is not consistent, but should be ...