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

后端 未结 6 2104
梦毁少年i
梦毁少年i 2020-12-19 04:45

For example, in JavaScript we could write a program like this:

var a = 1;
testFunction(++a, ++a, a);
function testFunc         


        
相关标签:
6条回答
  • 2020-12-19 05:24

    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.

    0 讨论(0)
  • 2020-12-19 05:25

    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'
    
    0 讨论(0)
  • 2020-12-19 05:30

    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        
    
    0 讨论(0)
  • 2020-12-19 05:40

    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
    
    0 讨论(0)
  • 2020-12-19 05:45
    >>> def f(x, y): pass
    ...
    >>> f(print(1), print(2))
    1
    2
    
    0 讨论(0)
  • 2020-12-19 05:45

    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
    
    0 讨论(0)
提交回复
热议问题