Python 无法将非运算符绑定到一元操作数

发布于 2025-01-19 19:18:54 字数 446 浏览 1 评论 0原文

在Python中,如果“不”操作员遵循一个位操作员(例如'&'或'|'),则结果是语法错误。当然,这将是二进制值的位操作,但这应该是可以的。就我记得而言,C中没有问题。

例如,这起作用:

a = 0
b = 1
anot = not(a)
bnot = not(b)
c = anot | bnot

但这是失败的:

c = not(a) | not(b)

这些工作:

c = not(a) | (not(b))   
c = not a | (not b)  

有人可以让我见解为什么要这么做吗?不寻找解决方法,只是对实施的解释。同时,我将努力了解源代码和CFG,以查看是否可以了解更多信息。到目前为止,我还没有在堆栈或其他Google上找到任何类似的问题。谢谢!

In Python, if a 'not' operator follows a bitwise operator (such as '&' or '|') the result is a syntax error. Granted that it will be a bitwise operation on a binary value, but that should be OK. There is no issue in C as far as I recall.

For example, this works:

a = 0
b = 1
anot = not(a)
bnot = not(b)
c = anot | bnot

but this fails:

c = not(a) | not(b)

these work:

c = not(a) | (not(b))   
c = not a | (not b)  

Can anyone give me insight as to why this should be? Not looking for workarounds, just an explanation of the implementation. In the meantime, I will struggle through the source code and CFGs to see if I can learn more. So far, I have not found any similar question on the Stacks or other Googles. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

云醉月微眠 2025-01-26 19:18:55

请记住,not 是关键字,而不是函数。因此表达式 not(a) 在语义上等同于 not a。前两个示例中的括号对绑定运算符没有任何帮助。但第三个示例中的 (not a) 将强制首先对内部表达式求值。

Remember that not is a keyword, not a function. So the expression not(a) is semantically equivalent to not a. The parens in your first two examples do nothing to help bind the operator. But the (not a) in the third example will force the evaluation of the inner expression to happen first.

贱人配狗天长地久 2025-01-26 19:18:55

Python语法确实清楚地表明了正在发生的事情:(我编辑了不同比较运算符的长列表,除非非终端名称和操作员本身,

inversion:
    | 'not' inversion 
    | comparison
comparison:
    | bitwise_or compare_op_bitwise_or_pair+ 
    | bitwise_or
compare_op_bitwise_or_pair:
    | eq_bitwise_or
    # ...
eq_bitwise_or: '==' bitwise_or
# ...
bitwise_or:
    | bitwise_or '|' bitwise_xor 
    | bitwise_xor
bitwise_xor:
    | bitwise_xor '^' bitwise_and 
    | bitwise_and
bitwise_and:
    | bitwise_and '&' shift_expr 
    | shift_expr

而不是必须是比较,或者是比较的优先链中的东西。并且|的操作数必须为bitwise_orbitwise_xor右侧),或者是这些优先链的东西。由于bitwise_or不是更远的链条,因此a bitwise_or表达式可以是不是而不是的操作数代码>不表达式不能是|的操作数中的任何一个。

所以不是0 | 1表示不是(0 | 1),因为0 | 1可以是不是的操作数,而不是0不能是|的操作数。和0 |不是1是语法错误,因为不是1不能是|的操作数,并且没有其他方法来解析表达式。

请注意,这是不是与C相同的C。在C中,所有Unary前缀操作员比任何二进制操作员都更紧密地绑定,因此!0 | 1表示> (!0)| 1,是1。这与python表达式而不是0 |相反。 1,是false

当然,这不是为什么 python语法的解释,而我并不能完全对推理进行全面的历史性描述。显然,认为意思是

    not a < b

不是(a&lt; b)而不是(不是a)&lt; B。后者的解释很少是必需的,因此它具有一定的意义。同样,这与其他布尔运营商的工作方式一致。 a&lt; B和B&lt; C实际上意味着幼稚的读者可能会期望什么。在C中也是如此:a&lt; B&amp;&amp; B&lt; C不需要括号来提供预期的解析。 (但是请注意,在C,&amp;|中,与Python的运营商的优先列表中不在同一位置。)

因此,这都是可以理解的,但是问题是为什么写语法以禁止像1 |这样的明确表达。不是,它只能以一种方式解析,无论其优先级如何。在这里,我只能猜测。

当然,可以编写语法,该语法允许这样的明确的表达方式。但是,这并不容易,如果您将自己限制在简单的BNF上(甚至现在在Python语法中使用的扩展BNF变体)。问题是级联优先样式不允许循环。如果先例没有形成一致的部分订单,则解析器报告歧义。另一方面,如果您使用类似YACC/野牛的解析器,或通过搜索该短语找到的许多操作员解析技术中的任何一个,那么这一点都不困难。因此,在没有基于优先权的歧义的情况下使用解析器生成器的决定可能与实施有关。

您遇到的歧义性的含糊不清是一项单位运算符的含义,当人们试图编写包括let表达式的语言的语法时,人们通常会遇到这种歧义:“ let”&lt; var&gt; “ =”&lt; expr&gt; “在”&lt; expr&gt;中。在该构造中,第二个&lt; expr&gt;是贪婪的:它可以扩展到可以扩展。但是,没有明显的原因表达式本身在操作员的右侧不应合法:

z = 3 * let x = 6 in x - 1/6

LET表达式评估为29/6 (6-(1/6)),因此有充分的理由相信z将为14.5,而不是报告语法错误的解析器。但是,使用天真编写的语法,您要么获得语法错误或一些奇怪的歧义报告。当语法实现时,您会得到语法错误,就像python实现不是的方式相同,并且出于相同的原因:let表达式无法在任一侧成为*的操作数。

如果您尝试修改级联优先语法以允许在**的右侧允许let,则通常会出现新的歧义。当解析器到达-时,它可以选择终止乘法 (3 *令x = 6 in x)-1/6或让吸收表达式的其余部分,3 *(令x = 6 in x -1/6中)。我不认为大多数人类读者会期望第一次解析,尽管您永远不知道,但是解析器并不能以人类的直觉运作。 (这通常是一件好事。)

对于操作员预言解析器来说,这是微不足道的,因为您要做的就是定义LET,左侧最高优先级,右侧最低优先级。 操作员本身一直在解析器堆栈上,直到解析器被迫将其弹出为止,因为它到达了表达式或近亲括号的末端,从而有效地“隐藏”*操作员。因此,一切都按预期工作。

The Python grammar does clearly indicate what's going on: (I edited out the long list of different comparison operators, which are all the same except for the non-terminal name and the operator itself)

inversion:
    | 'not' inversion 
    | comparison
comparison:
    | bitwise_or compare_op_bitwise_or_pair+ 
    | bitwise_or
compare_op_bitwise_or_pair:
    | eq_bitwise_or
    # ...
eq_bitwise_or: '==' bitwise_or
# ...
bitwise_or:
    | bitwise_or '|' bitwise_xor 
    | bitwise_xor
bitwise_xor:
    | bitwise_xor '^' bitwise_and 
    | bitwise_and
bitwise_and:
    | bitwise_and '&' shift_expr 
    | shift_expr

So, the operand for not must be a comparison, or something down the precedence chain from comparison. And the operands for | must be bitwise_or (bitwise_xor on the right) or something down the precedence chain for those. Since bitwise_or is further down the chain than not, a bitwise_or expression can be the operand of not but a not expression cannot be either of the operands of |.

So not 0 | 1 means not (0 | 1), because 0 | 1 can be the operand of not while not 0 cannot be an operand of |. And 0 | not 1 is a syntax error because not 1 cannot be an operand of | and there's no other way of parsing the expression.

Note that this is not the same as C. In C, all of the unary prefix operators bind more tightly than any binary operator, so !0|1 means (!0) | 1, which is 1. That's opposite to the Python expression not 0 | 1, which is False.

Of course, that's not an explanation for why the Python grammar is written that way, and I'm not in a position to give a complete historic account of the reasoning. Apparently, it was considered desirable that

    not a < b

mean not (a < b), rather than (not a) < b. The latter interpretation would very rarely be desired, so it makes a certain amount of sense. Also, that's consistent with how the other boolean operators work; a < b and b < c does in fact mean what a naïve reader would probably expect. And that's true in C, as well: a < b && b < c doesn't need to be parenthesised to provide the intended parse. (But note that in C, & and | are not in the same place in the precedence list as Python's operators with the same names.)

So that's all somewhat understandable, but the question is why the grammar is written so as to prohibit unambiguous expressions like 1 | not a, which can only be parsed in one way regardless of precedence. Here, I can only guess.

Certainly, it is possible to write a grammar which allows unambiguous expressions like that. But it's not easy, if you're limiting yourself to simple BNF (or even the extended BNF variant now used in the Python grammar). The problem is that the cascading precedence style doesn't allow loops; if precedences don't form a consistent partial order, the parser reports ambiguities. On the other hand, if you use a Yacc/Bison-like parser generator, or any of the many operator-precedence parsing techniques you'll find by searching for that phrase, then it's not difficult at all. So the decision to use a parser generator without precedence-based disambiguation is probably related to the implementation.

The kind of ambiguity you run into with lower precedence unary operators is the following, which people usually run into when they try to write a grammar for languages which include let expressions: "let" <var> "=" <expr> "in" <expr>. In that construct, the second <expr> is greedy: it extends as far as it can be extended. But there's no obvious reason why the let expression itself shouldn't be legal on the right-hand side of an operator:

z = 3 * let x = 6 in x - 1/6

The let expression evaluates to 29/6 (6 - (1 / 6)), so there's every reason to believe that z will be 14.5, rather than the parser reporting a syntax error. With a naively-written grammar, though, you either get the syntax error or some odd ambiguity report. You get the syntax error when the grammar implements let in the same way that Python implements not, and for the same reason: the let expression cannot be the operand of *, on either side.

If you try to modify the cascading precedence grammar to allow let on the right-hand side of *, you typically end up with a new ambiguity; when the parser reaches the -, it has the choice of terminating the multiplication ( 3 * let x = 6 in x) - 1/6 or letting the let absorb the rest of the expression, 3 * (let x = 6 in x - 1/6). I don't think most human readers would expect the first parse, although you never know, but a parser doesn't operate with human intuitions. (That's usually a good thing.)

This is trivial with an operator-precedence parser, because all you need to do is to define let with highest precedence on the left and lowest precedence on the right. The let operator itself stays on the parser stack until the parser is forced to pop it off, because it reaches the end of the expression or a close parenthesis, which effectively "hides" the precedence of the * operator. Thus, everything works as expected.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文