问题
For example, in JavaScript we could write a program like this:
var a = 1;
testFunction(++a, ++a, a);
function testFunction(x, y, z){
document.writeln("<br />x = " + x);
document.writeln("<br />y = " + y);
document.writeln("<br />z = " + z);
}
and we would get an output:
x = 2
y = 3
z = 3
This implies that parameters are truly evaluated from left to right in JavaScript. In C we would get output
x = 3
y = 3
z = 3
I was wondering if we could do the same in Python or is it impossible since it's a pass by value reference language?
I've made a simple program but I don't think that proves anything:
x = 2
def f(x, y, z):
print(x, y, z)
f(x*2, x*2, x**2)
print(x)
4 4 4
2
Python won't let me do any new assignment within the function parameter when I call it (for example f(x=4, x, x)
or something like this).
回答1:
>>> def f(x, y): pass
...
>>> f(print(1), print(2))
1
2
回答2:
Disassemble the function call.
>>> def foo():
... bar(x+1, x+2, x+3)
...
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (bar)
3 LOAD_GLOBAL 1 (x)
6 LOAD_CONST 1 (1)
9 BINARY_ADD
10 LOAD_GLOBAL 1 (x)
13 LOAD_CONST 2 (2)
16 BINARY_ADD
17 LOAD_GLOBAL 1 (x)
20 LOAD_CONST 3 (3)
23 BINARY_ADD
24 CALL_FUNCTION 3
27 POP_TOP
28 LOAD_CONST 0 (None)
31 RETURN_VALUE
回答3:
Using Python 3:
>>> a = []
>>> f = print(
a.append(1), a[:],
a.append(2), a[:],
a.append(3), a[:]
)
None [1] None [1, 2] None [1, 2, 3]
Archive:
>>> a = []
>>> f = print(a.append(1), a, a.append(2), a, a.append(3), a)
Curiously enough (at first), this code produces:
None [1, 2, 3] None [1, 2, 3] None [1, 2, 3]
However, dis(f)
makes this clearer:
>>> dis(f)
1 0 LOAD_NAME 0 (print) #Loads the value of 'print' into memory. Precisely, the value is pushed to the TOS (Top of Stack)
--> 3 LOAD_NAME 1 (a) #Loads the value of object 'a'
6 LOAD_ATTR 2 (append) #Loads the append attribute (in this case method)
9 LOAD_CONST 0 (1) #Loads the constant 1
12 CALL_FUNCTION 1 #a.append(1) is called
15 LOAD_NAME 1 (a) #for print(...,a,...)
18 LOAD_NAME 1 (a) #for the next a.append()
21 LOAD_ATTR 2 (append)
24 LOAD_CONST 1 (2)
27 CALL_FUNCTION 1 #a.append(2)
30 LOAD_NAME 1 (a)
33 LOAD_NAME 1 (a)
36 LOAD_ATTR 2 (append)
39 LOAD_CONST 2 (3)
42 CALL_FUNCTION 1 #a.append(3)
45 LOAD_NAME 1 (a) #loads a to be used thrice by print
48 CALL_FUNCTION 6 #calls print
51 PRINT_EXPR #prints TOS and clears it
52 LOAD_CONST 3 (None) #Loads None
55 RETURN_VALUE #Returns None
The output of dis(f)
is what we expected - L-to-R evaluation. Essentially, this "discrepancy" is a consequence of print()
being evaluated last. By then, the value of a
has changed to [1, 2, 3]
and the same final object is printed thrice.
If we replace a
with a[:]
, we get the expected result.
回答4:
A custom class can help here:
class Tester(object):
"test object to reveal left to right evaluation"
def __init__(self, value):
self.value = value
def __add__(self, value):
print("adding ", value)
return Tester(self.value + value)
def __repr__(self):
return repr(self.value)
and when run:
--> t = Tester(7)
--> t
7
--> t = t + 7
adding 7
--> t
14
--> def blah(a, b, c):
... print(a, b, c)
...
--> blah(t+1, t+2, t+3)
adding 1
adding 2
adding 3
15 16 17
回答5:
This shows it as well IMHO:
>>> '{} {} {}'.format(x,x+1,x+2)
'1 2 3'
Edit:
>>> def f(t): return time.time()-t
...
>>> t1=time.time(); '{:.4} {:.4} {:.4}'.format(f(t1),f(t1),f(t1))
'5.007e-06 7.868e-06 9.06e-06'
回答6:
Short answer: left to right
Example: Since this is a question that always pops in my head when I am doing arithmetic operations (should I convert to float and which number), an example from that aspect is presented:
>>> a = 1/2/3/4/5/4/3
>>> a
0
When we divide integers, not surprisingly it gets lower rounded.
>>> a = 1/2/3/4/5/4/float(3)
>>> a
0.0
If we typecast the last integer to float, we will still get zero, since by the time our number gets divided by the float has already become 0 because of the integer division.
>>> a = 1/2/3/float(4)/5/4/3
>>> a
0.0
Same scenario as above but shifting the float typecast a little closer to the left side.
>>> a = float(1)/2/3/4/5/4/3
>>> a
0.0006944444444444445
Finally, when we typecast the first integer to float, the result is the desired one, since beginning from the first division, i.e. the leftmost one, we use floats.
Extra 1: If you are trying to answer that to improve arithmetic evaluation, you should check this
Extra 2: Please be careful of the following scenario:
>>> a = float(1/2/3/4/5/4/3)
>>> a
0.0
来源:https://stackoverflow.com/questions/12749246/how-to-prove-that-parameter-evaluation-is-left-to-right-in-python