views:

196

answers:

3

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

Can i rely in 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.

+11  A: 

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).

Amber
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.
Thomas Wouters
Wrong about assignment, pls change example! The only operator in Python that associated right-to-left is `**`, as in `2**3**4`
Nas Banov
@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).
John Machin
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.
Amber
@Amber: because the 100+ rep points are undeserved
John Machin
@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.
Amber
@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).
John Machin
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.
Amber
+3  A: 

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
Dave Webb
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()`
Thomas Wouters
I didn't mean to imply assignment was an operator, this is why it's last. I've slightly changed the wording about assignment.
Dave Webb
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.
Thomas Wouters
That wording comes directly from the Python documentation: http://docs.python.org/reference/expressions.html#evaluation-order
Dave Webb
+11  A: 

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.

msw
I would give +10 to this answer if I could. :) This is a very important point.
EOL
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.
msw
why do you assume that i am trying to write code? what if... i am trying to read.
Lord British
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.
John Machin
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.
Paddy3118
@msw: i added a comment in the question.
Lord British
@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".
John Machin