简体   繁体   English

如何证明参数评估在Python中“从左到右”?

[英]How to prove that parameter evaluation is “left to right” in Python?

For example, in JavaScript we could write a program like this: 例如,在JavaScript中我们可以编写这样的程序:

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. 这意味着在JavaScript中从左到右真正地评估参数。 In C we would get output 在C中我们会得到输出

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? 我想知道我们是否可以在Python中做同样的事情,或者它是不可能的,因为它是价值参考语言的传递?

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). 当我调用它时,Python不会让我在函数参数中做任何新的赋值(例如f(x=4, x, x)或类似的东西)。

>>> def f(x, y): pass
...
>>> f(print(1), print(2))
1
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        

Using Python 3: 使用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)使这更清楚:

>>> 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. dis(f)的输出是我们预期的 - L-to-R评估。 Essentially, this "discrepancy" is a consequence of print() being evaluated last. 从本质上讲,这种“差异”是最后评估print()的结果。 By then, the value of a has changed to [1, 2, 3] and the same final object is printed thrice. 届时,的值a已经变为[1, 2, 3]和相同的最终输出对象三次。

If we replace a with a[:] , we get the expected result. 如果我们用a[:]替换a ,我们得到预期的结果。

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'

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

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: 示例:由于这是一个在我进行算术运算时总会出现在我头脑中的问题(我应该转换为float和哪个数字),这个方面的例子如下:

>>> 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. 如果我们将最后一个整数强制转换为float,我们仍然会得到零,因为当我们的数字除以浮点时,由于整数除法,它已经变为0。

>>> 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, ie the leftmost one, we use floats. 最后,当我们将第一个整数强制转换为float时,结果是所需的整数,因为从第一个分区开始,即最左边的整数,我们使用浮点数。

Extra 1: If you are trying to answer that to improve arithmetic evaluation, you should check this 额外1:如果您试图回答这个问题以改进算术评估,您应该检查一下

Extra 2: Please be careful of the following scenario: 额外2:请注意以下情况:

>>> a = float(1/2/3/4/5/4/3)
>>> a
0.0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM