Associativity of “in” in Python?

后端 未结 4 724
既然无缘
既然无缘 2021-01-30 15:15

I\'m making a Python parser, and this is really confusing me:

>>>  1 in  []  in \'a\'
False

>>> (1 in  []) in \'a\'
TypeError: \'in &         


        
相关标签:
4条回答
  • 2021-01-30 15:57

    1 in [] in 'a' is evaluated as (1 in []) and ([] in 'a').

    Since the first condition (1 in []) is False, the whole condition is evaluated as False; ([] in 'a') is never actually evaluated, so no error is raised.

    Here are the statement definitions:

    In [121]: def func():
       .....:     return 1 in [] in 'a'
       .....: 
    
    In [122]: dis.dis(func)
      2           0 LOAD_CONST               1 (1)
                  3 BUILD_LIST               0
                  6 DUP_TOP             
                  7 ROT_THREE           
                  8 COMPARE_OP               6 (in)
                 11 JUMP_IF_FALSE            8 (to 22)  #if first comparison is wrong 
                                                        #then jump to 22, 
                 14 POP_TOP             
                 15 LOAD_CONST               2 ('a')
                 18 COMPARE_OP               6 (in)     #this is never executed, so no Error
                 21 RETURN_VALUE         
            >>   22 ROT_TWO             
                 23 POP_TOP             
                 24 RETURN_VALUE        
    
    In [150]: def func1():
       .....:     return (1 in  []) in 'a'
       .....: 
    
    In [151]: dis.dis(func1)
      2           0 LOAD_CONST               1 (1)
                  3 LOAD_CONST               3 (())
                  6 COMPARE_OP               6 (in)   # perform 1 in []
                  9 LOAD_CONST               2 ('a')  # now load 'a'
                 12 COMPARE_OP               6 (in)   # compare result of (1 in []) with 'a'
                                                      # throws Error coz (False in 'a') is
                                                      # TypeError
                 15 RETURN_VALUE   
    
    
    
    In [153]: def func2():
       .....:     return 1 in ([] in 'a')
       .....: 
    
    In [154]: dis.dis(func2)
      2           0 LOAD_CONST               1 (1)
                  3 BUILD_LIST               0
                  6 LOAD_CONST               2 ('a') 
                  9 COMPARE_OP               6 (in)  # perform ([] in 'a'), which is 
                                                     # Incorrect, so it throws TypeError
                 12 COMPARE_OP               6 (in)  # if no Error then 
                                                     # compare 1 with the result of ([] in 'a')
                 15 RETURN_VALUE        
    
    0 讨论(0)
  • 2021-01-30 16:10

    Python does special things with chained comparisons.

    The following are evaluated differently:

    x > y > z   # in this case, if x > y evaluates to true, then
                # the value of y is being used to compare, again,
                # to z
    
    (x > y) > z # the parenth form, on the other hand, will first
                # evaluate x > y. And, compare the evaluated result
                # with z, which can be "True > z" or "False > z"
    

    In both cases though, if the first comparison is False, the rest of the statement won't be looked at.

    For your particular case,

    1 in [] in 'a'   # this is false because 1 is not in []
    
    (1 in []) in a   # this gives an error because we are
                     # essentially doing this: False in 'a'
    
    1 in ([] in 'a') # this fails because you cannot do
                     # [] in 'a'
    

    Also to demonstrate the first rule above, these are statements that evaluate to True.

    1 in [1,2] in [4,[1,2]] # But "1 in [4,[1,2]]" is False
    
    2 < 4 > 1               # and note "2 < 1" is also not true
    

    Precedence of python operators: http://docs.python.org/reference/expressions.html#summary

    0 讨论(0)
  • 2021-01-30 16:14

    From the documentation:

    Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

    What this means is, that there no associativity in x in y in z!

    The following are equivalent:

    1 in  []  in 'a'
    # <=>
    middle = []
    #            False          not evaluated
    result = (1 in middle) and (middle in 'a')
    
    
    (1 in  []) in 'a'
    # <=>
    lhs = (1 in []) # False
    result = lhs in 'a' # False in 'a' - TypeError
    
    
    1 in  ([] in 'a')
    # <=>
    rhs = ([] in 'a') # TypeError
    result = 1 in rhs
    
    0 讨论(0)
  • 2021-01-30 16:14

    The short answer, since the long one is already given several times here and in excellent ways, is that the boolean expression is short-circuited, this is has stopped evaluation when a change of true in false or vice versa cannot happen by further evaluation.

    (see http://en.wikipedia.org/wiki/Short-circuit_evaluation)

    It might be a little short (no pun intended) as an answer, but as mentioned, all other explanation is allready done quite well here, but I thought the term deserved to be mentioned.

    0 讨论(0)
提交回复
热议问题