为什么 `0--3//2` 和 `--3//2` 之间有区别?

Kar*_*elm 63 python operator-precedence

我正在研究如何在没有math模块的情况下进行地板/天花板操作。我通过使用楼层除法解决了这个问题//,并发现负数“给出了上限”。所以这有效:

>>> 3//2
1
>>> -3//2
-2
Run Code Online (Sandbox Code Playgroud)

我希望答案是肯定的,所以首先我尝试了--3//2,但这给出了 1。我推断这是因为 Python 的计算结果--+。所以为了解决这个问题,我发现我可以使用-(-3//2)),问题就解决了。

但我找到了另一个解决方案,即(我包含了前面的示例进行比较):

>>> --3//2  # Does not give ceiling
1
>>> 0--3//2  # Does give ceiling
2
Run Code Online (Sandbox Code Playgroud)

我无法解释为什么包含 0 有帮助。我已阅读有关除法的文档,但没有找到任何帮助。我认为这可能是因为评估顺序:

如果我使用--3//2作为示例,从我所拥有的文档中,该示例Positive, negative, bitwise NOT在该示例中是最严格的,并且我猜其计算结果--+. 接下来是Multiplication, division, remainder,所以我猜这就是+3//2评估结果1,我们就完成了。我无法从文档中推断出为什么包含0应该改变结果。

参考:

Mis*_*agi 85

Python 使用该符号-作为一( -x) 和二元( x-y) 运算符。它们具有不同的运算符优先级

具体来说,排序方式//是:

  • 一元-
  • 二进制//
  • 二进制-

通过引入0as 0--3//2,第一个-二进制文件 -并且最后应用。如果没有前导0as --3//2,两者-都是一元并且一起应用。

相应的评估/语法树大致是这样的,首先评估底部的节点以在父节点中使用它们:

 ---------------- ---------------- 
|     --3//2     |    0--3//2     |
|================|================|
|                |    -------     |
|                |   | 0 - z |    |
|                |    -----+-     |
|                |         |      |
|     --------   |     ----+---   |
|    | x // y |  |    | x // y |  |
|     -+----+-   |     -+----+-   |
|      |    |    |      |    |    |
|  ----+    +--  |   ---+    +--  |
| | --3 |  | 2 | |  | -3 |  | 2 | |
|  -----    ---  |   ----    ---  |
 ---------------- ---------------- 
Run Code Online (Sandbox Code Playgroud)

因为一元运算-一起应用,所以它们抵消了。相反,一元和二元分别在除法之前之后-应用。

  • @KarlWilhelm 这是在代码中添加空格和括号将使发生的事情更加清晰的地方。除非您正在为 CodeGolf.SE 编写,并试图节省字节,否则最好更干净地格式化这样的内容...... (21认同)
  • 啊,我是个新手,所以我迷失了“unary”和“binary”之间的区别。在您回答之后,我尝试了以下操作:“0+--3//2 == 1”,因此“0”右侧的第一个运算符被视为“二元”运算符是有意义的。 (8认同)
  • @CarstenS 从语法上讲,它不是字面量的一部分 - AST 基本上将其表示为“UnaryOp("-", 3)``(只是不太漂亮)。[整数文字只是绝对部分](https://docs.python.org/3/reference/lexical_analysis.html#integer-literals)。但是,它在生成字节码时进行评估,并在运行时直接加载为值“-3”。 (7认同)
  • @KarlWilhelm 一元运算是一种带有一个参数的函数。二元运算是一个需要两个值的函数。我发现在解释发生的情况时画括号很有帮助。`0--3//2` 是 `0 - (-3 // 2)`,而 `--3//2` 是 `(--3) // 2` (5认同)
  • 现在我想知道在Python中“-3”中的“-”是一元运算符还是整数文字的一部分。 (2认同)

Kar*_*tel 30

这是一个简单的操作顺序问题。

--3//2是相同的(-(-3)) // 2。由于左侧没有任何内容,因此每个都-必须是一元否定;并且这比//;具有更高的优先级 so3被求反两次(得到 3),然后除以 2。

0--3//2是相同的0 - ((-3) // 2)。既然左边有东西,那么第一个-一定是二进制减法,它的优先级比//。第二个-仍然是一元否定;-3除以2yield -2,然后从 中减去该值0


Ter*_*edy 18

了解 CPython 实际如何计算的另一种方法是使用 dis 模块来查看它对其堆栈机器的实际执行情况。

>>> import dis
>>> dis.dis('0--3//2')
  1           0 LOAD_CONST               0 (2)
              2 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

哎呀,常量是在编译期间计算的,所以使用一个名称。

>>> t=3
>>> dis.dis('0--t//2')
  1           0 LOAD_CONST               0 (0)
              2 LOAD_NAME                0 (t)
              4 UNARY_NEGATIVE
              6 LOAD_CONST               1 (2)
              8 BINARY_FLOOR_DIVIDE
             10 BINARY_SUBTRACT
             12 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)