109

我正在制作一个 Python 解析器,这真的让我很困惑:

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

>>> (1 in []) in 'a'
TypeError: 'in <string>' requires string as left operand, not bool

>>> 1 in ([] in 'a')
TypeError: 'in <string>' requires string as left operand, not list

in在关联性等方面,Python究竟是如何工作的?

为什么这些表达式中没有两个表现相同?

4

4 回答 4

124

1 in [] in 'a'被评估为(1 in []) and ([] in 'a')

由于第一个条件 ( 1 in []) 是False,因此整个条件被评估为False; ([] in 'a')从未实际评估过,因此不会引发错误。

我们可以看到 Python 如何使用dis模块执行每条语句:

>>> from dis import dis
>>> dis("1 in [] in 'a'")
  1           0 LOAD_CONST               0 (1)
              2 BUILD_LIST               0
              4 DUP_TOP
              6 ROT_THREE
              8 CONTAINS_OP              0        # `in` is the contains operator
             10 JUMP_IF_FALSE_OR_POP    18        # skip to 18 if the first 
                                                  # comparison is false
             12 LOAD_CONST               1 ('a')  # 12-16 are never executed
             14 CONTAINS_OP              0        # so no error here (14)
             16 RETURN_VALUE
        >>   18 ROT_TWO
             20 POP_TOP
             22 RETURN_VALUE
>>> dis("(1 in []) in 'a'")
  1           0 LOAD_CONST               0 (1)
              2 LOAD_CONST               1 (())
              4 CONTAINS_OP              0        # perform 1 in []
              6 LOAD_CONST               2 ('a')  # now load 'a'
              8 CONTAINS_OP              0        # check if result of (1 in []) is in 'a'
                                                  # throws Error because (False in 'a')
                                                  # is a TypeError
             10 RETURN_VALUE
>>> dis("1 in ([] in 'a')")
  1           0 LOAD_CONST               0 (1)
              2 BUILD_LIST               0
              4 LOAD_CONST               1 ('a')
              6 CONTAINS_OP              0        # perform ([] in 'a'), which is 
                                                  # incorrect, so it throws a TypeError
              8 CONTAINS_OP              0        # if no Error then this would 
                                                  # check if 1 is in the result of ([] in 'a')
             10 RETURN_VALUE

  1. 除了[]只评估一次。在此示例中这无关紧要,但如果您(例如)替换[]为返回列表的函数,则该函数只会被调用一次(最多)。该文档也解释了这一点。
于 2012-09-30T11:35:18.787 回答
24

Python 通过链式比较做了一些特殊的事情。

以下是不同的评估:

x > y > z   # in this case, if x > y evaluates to true, then
            # the value of y is used, again, and compared with z

(x > y) > z # the parenthesized 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"

但是,在这两种情况下,如果第一个比较是False,则不会查看语句的其余部分。

对于您的特定情况,

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'

同样为了演示上面的第一条规则,这些是评估为 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

Python 运算符的优先级:https ://docs.python.org/3/reference/expressions.html#comparisons

于 2012-09-30T11:41:59.630 回答
11

从文档中:

比较可以任意链接,例如,x < y <= z 等价于 x < y 和 y <= z,除了 y 只计算一次(但在这两种情况下,当找到 x < y 时根本不计算 z是假的)。

这意味着,没有关联性x in y in z

以下是等价的:

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
于 2012-09-30T12:06:45.287 回答
3

简短的回答,因为 long 已经在这里多次给出并且以极好的方式给出,是 boolean 表达式是 short-circuited,当进一步评估不能发生 true 到 false 的变化或反之亦然时,这已经停止评估。

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

作为答案,它可能有点短(没有双关语),但如前所述,所有其他解释在这里都做得很好,但我认为这个词值得一提。

于 2012-09-30T19:16:38.417 回答