Python operator precedence
Asked Answered
R

3

9

The Python docs say that * and / have the same precedence.
I know that expressions in python are evaluated from left to right.

Can i rely on that and assume that j*j/m is always equal to (j*j)/m avoiding the parentheses?
If this is the case can i assume that this holds for operators with the same precedence in general?


ps: The question as it is fine for my purposes, i came to it while reading integer-only code (like the above example) without parentheses, which at the time looked a lot suspicious to me.

Rabon answered 25/7, 2010 at 6:54 Comment(4)
"Explicit is better than implicit.", "In the face of ambiguity, refuse the temptation to guess."- PEP 20Unpaged
british: If you were reading "integer-only code", you have a much bigger problem: What version of Python was intended to be used to run it? Does the code have from __future__ import division up the front? Should that / be read as and/or changed to //?Oenone
@John Machin: / means integer division in that case, is python 2.6 without the from future stuff, i noted that the codes like jj/m (jj)/m where equal, but was afraid about the consistency if this behavior.Rabon
now i can read and understand what code like this mean jm/jj/j.Rabon
D
14

Yes - different operators with the same precedence are left-associative; that is, the two leftmost items will be operated on, then the result and the 3rd item, and so on.

An exception is the ** operator:

>>> 2 ** 2 ** 3
256

Also, comparison operators (==, >, et cetera) don't behave in an associative manner, but instead translate x [cmp] y [cmp] z into (x [cmp] y) and (y [cmp] z).

Disaccustom answered 25/7, 2010 at 6:56 Comment(8)
Assignment is actually not an expression in Python, and = not an operator in the normal sense; operator precedence doesn't apply. Assignment to multiple targets is explicitly defined in the grammar to be exactly that: assigning the same object to multiple targets.Skep
Wrong about assignment, pls change example! The only operator in Python that associated right-to-left is **, as in 2**3**4Chengteh
@Amber: (1) as @Nas Banov has said, ** is the ONLY Python operator that's right-associative (2) that it's right-associative with itself is the unremarkable outcome of fact [1] (3) comparison operators are NON-associative ... a < b < c means neither (a < b) < c nor a < (b < c) --- please consider the ultimate edit (deleting your answer).Oenone
Why delete an answer when it could be edited to be improved? Not to mention, you have enough rep that you could have simply edited it yourself.Disaccustom
@John: Honestly, I don't do this for rep; even if I did, I'm pretty sure I hit the daily rep cap regardless of this question. I don't see the point in deleting what is now a concise and correct answer to the OP's question.Disaccustom
@Amber: but it's not correct; you don't answer the OP's first question ("can I rely") and give an incorrect generalisation (see my comment re comparison operators) as answer to his second question (can I assume).Oenone
I'm pretty sure "Yes" is a pretty clear answer to the OP's first question. Comparisons are an edge case, but it's simple enough to add a note about that.Disaccustom
... except that y is only evaluated once.Ketchan
U
14

But, if it is ambiguous to you - the coder - and it must be because you have to ask, then expect it will be at least as ambiguous for the reader and waste a couple octets for clarity.

Relying on precedence rules is great if you happen to be a compiler.

added responses to comments:

For the person reading code who encounters an ambiguity that requires outside consultation for assurance, you should assume that the next reader will be less savvy than you and save them the effort and avoidable human error of parsing the same construct and add the parenthesis for them.

As it happens, even the accepted answer was incorrect (in rationale, not effect, see its first comment) which I wasn't aware of and neither were a fraction of those who upvoted it.

As to the statement about basic algebra, the particular example used in the OP is instructive. Regardless of operator precedence the expression j * (j / m) is algebraically identical to (j * j) / m. Unfortunately, Python algebra is only an approximation of "Platonic ideal" algebra which could yield incorrect answers for either form depending on the magnitudes of j and m. For example:

>>> m = 1e306
>>> m
1e+306
>>> j = 1e307
>>> j
9.9999999999999999e+306
>>> j / m
10.0
>>> j*j
inf
>>> j * (j / m)
1e+308
>>> (j * j) / m
inf
>>> ((j * j) / m) == (j * (j/m))
False

So indeed the identity property of Python's (and my FPU) quasi-algebra doesn't hold. And this may be different on your machine for as the documentation notes:

Floating point numbers are implemented using double in C. All bets on their precision are off unless you happen to know the machine you are working with.

It could be claimed that one has no business working on the hairy edge of overflow, and that's true to some extent, but removed from context the expression is indeterminate given one order of operations and "correct" under another.

Unpaged answered 25/7, 2010 at 7:29 Comment(7)
I would give +10 to this answer if I could. :) This is a very important point.Labialized
To get a mite philosophical, the accepted answer is indeed the correct answer to the question that was asked. Which must mean that the question is wrong, and it is at the point where it asks about "avoiding the parentheses". Having recently had to munge through a working yet tortuously unreadable library, I've become keenly sensitive to the difference in the express standards of Python and its use in common practice.Unpaged
why do you assume that i am trying to write code? what if... i am trying to read.Rabon
My two bits worth: expecting redundant parentheses in an expression involving only +-*/ operators is ridiculous ... the precedence and associativity are (implicitly) taught in school algebra classes, aren't they? Considered basic knowledge for a computer programmer, yes/no? On the other hand, using parentheses around ops like bit-shift, bit-and, bit-or, etc which are used infrequently and have different precedence in different languages should be mandatory in project or team standards.Oenone
If there is doubt then you should always use parenthesis. The OP is in effect saying that he was not sure of what precedence applies, so it is correct not to rely on them in this case.Helenahelene
@msw: i added a comment in the question.Rabon
@paddy3118: Nothing to do with "not sure". The OP is saying in effect that he has read the Python docs "Python docs say that * and / have the same precedence" but doubts the reliability of the implementation "Can I rely [on] that ... ?" -- a very different kettle of fish, to which the answer is "Yes, in Python as in [almost all] other languages".Oenone
Y
3

Short answer: yes.

The Python documentation says the following:

Operators in the same box have the same precedence. Unless the syntax is explicitly given, operators are binary. Operators in the same box group left to right (except for comparisons, including tests, which all have the same precedence and chain from left to right... and exponentiation, which groups from right to left).

So in other words the answer to your question is yes, operators with the same precedence will group left to right apart from Comparisions which chain rather than group:

>>> x = 0
>>> y = 0
>>> x == y == True
False
>>> (x == y) == True
True
>>> x == (y == True)
True

and Exponentation:

>>> 2 ** 2 ** 3
256
>>> (2 ** 2) ** 3
64
>>> 2 ** (2 ** 3)
256

Also, in assignment the right-hand side is evaluated before the left-hand side:

>>> x = 1
>>> y = x = 2
>>> y
2
Yonne answered 25/7, 2010 at 7:20 Comment(4)
Assignment is not an operator (see above), and the targets are in fact not evaluated right to left. The rhs of the assignment is evaluated, and then the targets are evaluated left to right and assigned to. You can see the distinction when you check the order of functioncalls in for example a().a, b().b = c().c: you'll see that c() gets called, then the c attribute is retrieved, then a() is called and the a attribute assigned to, and only then the same for b()Skep
I didn't mean to imply assignment was an operator, this is why it's last. I've slightly changed the wording about assignment.Yonne
Your wording still implies that the order of evaluation of x and y matters to the result assigned to y. It does not. The values of x and y are never involved in the assignment. y = x = 2 strictly means "assign 2 to y and to x", not "assign 2 to x, then the resulting value of x to y" (which, again, you can see when you involve more complex assignment targets, like when using properties that set themselves to different values than you assign to them.Skep
That wording comes directly from the Python documentation: docs.python.org/reference/expressions.html#evaluation-orderYonne

© 2022 - 2024 — McMap. All rights reserved.