Order of calculation - python

Example : 10 ** 4*3 ** 2
Our teacher said python first calculates 3**2 and then 10**4 and then multiplies the answers.
But if both side of the multiplication have same order of precedence and python reads from left to right, doesn’t it do 10**4 first and then 3**2?
(My question is not about the order or precedence of operations as both side have same order of precedence)

Your teacher is wrong. From https://docs.python.org/3/reference/expressions.html#evaluation-order:
6.15. Evaluation order
Python evaluates expressions from left to right. Notice that while
evaluating an assignment, the right-hand side is evaluated before the
left-hand side.
The * has two expressions as its operands, 10**4 and 3**2. The left operand, 10**4 is evaluated first, followed by the right operand, 3**2.
Your teacher may be confused with a chain of exponentiations like 2 ** 3 ** 4. Here the "outer" exponentiation has two arguments 2 and 3 ** 4 (rather than 2**3 and 4, because of right associativity). However, the 2 is still evaluated before 3**4.
For a simple expression like this, it doesn't really matter. But it could matter if one or both operands involved function calls with side effects. Consider something contrived like lambda: print(3) or 3)() + (lambda: print(4) or 4)(). The value of the expression is 7 either way, but the order in which the print functions are called will affect the output. Python, though, guarantees that 3 will be output before 4.
Some languages (notably C, IIRC) don't specify an order of evaluation, so it is an implementation detail whether the left or right is evaluated first. This means you can't predict the value of something like i=3; j=(i++)*i; j could be either 3*3 == 9 or 4*3 == 12, depending on whether i is incremented before or after the right-hand operand is evaluated. (I don't recall if this is considered undefined behavior or implementation-defined behavior, though.)

We can easily test it:
class C:
def __init__(self, v):
self.v = v
def __pow__(self, x):
print(f'eval {self.v} ** {x}')
return self.v ** x
>>> C(1) ** 2 * C(4) ** 5
eval 1 ** 2
eval 4 ** 5
1024
In short, your teacher is wrong, at least for Python 3.7. In any case, regardless of whether it's true, I'd never rely on the order here, since those assumptions make the code less readable.

Related

Precedence of operators in python [duplicate]

The Python documentation for operator precedence states:
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 — see section Comparisons...)
What does this mean? Specifically:
"Operators in the same box group left to right (except for
comparisons...)" -- do comparisons not group left to right?
If comparisons do not group left to right, what do they do instead? Do they "chain" as opposed to "group"?
If comparisons "chain" rather than "group", what is the difference between "chaining" and "grouping"?
What would be some examples to demonstrate that the comparison operators chain from left to right rather than from right to left?
Grouping (this is what non-comparison operators do):
a + b + c means (a + b) + c
Chaining (this is what comparison operators do):
a < b < c means (a < b) and (b < c)
Grouping left to right (this is the way things are grouped):
5 - 2 - 1 means (5 - 2) - 1 == 2
as opposed to grouping right to left (this would produce a different result):
5 - (2 - 1) == 4
Chaining left to right
Chaining is left to right, so in a < b < c, the expression a < b is evaluated before b < c, and if a < b is falsey, b < c is not evaluated.
(2 < 1 < f()) gives the value False without calling the function f, because 2 < 1 evaluates to false, so the second comparison does not need to be performed.
f() > 1 > g() calls f() in order to evaluate the first comparison, and depending on the result, it might or might not need to evaluate the second condition, which requires calling g().
NB. Each operand is evaluated at most once. So in the expression 1 < f() < 2, the function f() is only called once, and the value it gives is used in both comparisons (if necessary).
https://en.wikipedia.org/wiki/Short-circuit_evaluation
In fact, the chain behavior is not so obvious.
a == b == c
although one would expect this to be converted to
a == b and b == c
it is in fact converted into somthing similar to
b == c if a == b else False
which is a bit confusing if one tries to override the behavior of the comparison operators and chain them.

Condensing multiple operations to single line in Python [duplicate]

Why does this work
>> x, y = (1, 2)
>> print x, y
1 2
But augmenting results in syntax errors..
>> x, y -= (1, 2)
SyntaxError: illegal expression for augmented assignment
Is there a different way, I was expecting:
>> x, y -= (1, 2)
>> print x, y
0 0
You cannot use an augmented assignment statement on multiple targets, no.
Quoting the augmented assignment documentation:
With the exception of assigning to tuples and multiple targets in a single statement, the assignment done by augmented assignment statements is handled the same way as normal assignments. Similarly, with the exception of the possible in-place behavior, the binary operation performed by augmented assignment is the same as the normal binary operations.
Emphasis mine.
In-place augmented assignment is translated from target -= expression to target = target.__isub__(expression) (with corresponding __i...__ hooks for each operator) and translating that operation to multiple targets is not supported.
Under the hood, augmented assignment is a specialisation of the binary operators (+, *, -, etc), not of assignment. Because the implementation is based on those operators and binary operators only ever have two operands, multiple targets were never included in the original implementation proposal.
You'll have to simply apply the assignments separately:
x -= 1
y -= 2
or, if you really, really wanted to get convoluted, use the operator module and zip() to apply operator.isub to the combinations (via itertools.starmap(), then use tuple assignment:
from operator import sub
from itertools import starmap
x, y = starmap(operator.isub, zip((x, y), (1, 2)))
where isub will ensure that the right hook is called allowing for in-place subtraction for mutable types that support it.
or, if you are manipulating types that don't support in-place manipulation, using a generator expression suffices:
x, y = (val - delta for val, delta in zip((x, y), (1, 2)))
This x, y = (1, 2) is sequence assignment. It relies on the right hand side being an iterable object, and the left hand side being composed of the same number of variables as iterating the left hand side provides.
This x, y -= (1, 2) is attempt to call the method __isub__ on the left hand operand(s). The nature of an in-place ("augmented") assignment is that it must take a variable on its left hand side, whose value receives the operator call, and the variable then receives the result of that call. Python does not allow distribution of an in-place assignment over multiple targets.

Built in double-asterisk power function not working as expected [duplicate]

This question already has answers here:
Calculation error with pow operator
(4 answers)
Closed 5 years ago.
This prints -2.4:
print(-3**0.8)
But this prints an imaginary number...?
a = -3
b = 0.8
print(a**b)
I can't figure out what is causing this and this problem is breaking my program. How do you make it so that the second statement outputs the same as the first in a general case that works for positive and negative values for a?
Per the Python 3.6 documentation:
6.5. The power operator
The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right. The syntax is:
power ::= ( await_expr | primary ) ["**" u_expr]
Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands): -1**2 results in -1.
Thus, in the first example:
>>> -3 ** 0.8
-2.4082246852806923
It's evaluated right to left as ** has higher precedence than unary -, binding tighter -- so here, 3 ** 0.8 is evaluated first, then the unary - operator is applied for a negative value. In the second example however, the expression is equivalent (-3) ** 0.8 as -3 is stored in a name, and evaluation results is an imaginary number:
>>> a = -3
>>> b = 0.8
>>> a ** b
(-1.9482946966653392+1.4155189542146738j)
A solution would be to calculate the result without unary operators, then applying a sign as necessary, per Shakar Bhattarai's answer:
>>> int(a / abs(a)) * (abs(a) ** b)
-2.4082246852806923
The first part, int(a / abs(a)) evaluates to either -1.0 or 1.0 depending on if a is negative or not, basically applying the sign. It then multiplies that by the result of a ** b regardless of a's sign. That will first compute a ** b disregarding it's sign, then applying the sign as necessary. This will get rid of the discrepancy. You could apply the same concept with math.copysign:
>>> math.copysign(1, a) * (abs(a) ** b)
-2.4082246852806923
This will just copy the sign from a to 1, which will give -1.0 or 1.0 based on negativity.
This is because of the precedence of the power operator. The power operator binds tighter than unary operators. So:
-3 ** 0.8
is evaluated as
-(3 * 0.8)
You can see how Python parses your code using the ast module:
>>> import ast
>>> ast.dump(ast.parse('-3 ** 0.8'))
'Module(body=[Expr(value=UnaryOp(op=USub(), operand=BinOp(left=Num(n=3), op=Pow(), right=Num(n=0.8))))])'
>>>
In the above, the 3 ** 0.8 is treated as one expression with ** being the operator. The unary minus is then applied to the value of the inner expression.
However, in your second example the values are stored in variables, so the precedence does not affect the expression. So a ** b is equivlent to (-3) ** 0.8
The solution is to bind the unary minus to the three using parenthesis:
>>> (-3) ** 0.8
(-1.94829469666534+1.4155189542146727j)
>>>
The ** operator has the highest precedence over all other operators. Hence when you use
print(-3**0.8)
python first evaluates the exponential and then negates it.
But when you run
a = -3
b = 0.8
print(a**b)
a has implicitly been negated first. Hence the result is effectively a imaginary number (a negative number raised to a fractional exponent)
To solve your issue, you could do something like
print (int(a/abs(a))*(abs(a)**b))

Why does `-(num)**(even_number)` give `-(num^(even_number))` as result?

This is something weird I noticed.
Any particular reason why -5**2 gives -25 and math.pow(-5,2) gives 25? The answer should be 25. So what is the reason for the -25 answer?
>>> -5**2
-25
>>> -5**4
-625
>>> 5**2
25
>>> 5**4
625
>>> import math
>>> pow(-5,2)
25
>>> pow(-5,4)
625
>>>
In Python the ** operator has higher precedence than the - operator, so in your expression 5 ** 2 is evaluated first, then negated. You can apply negation first by using brackets:
>>> -5**2
-25
>>> (-5)**2
25
This is all explained in the documentation
The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right.
Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands): -1**2 results in -1.
The documentation for math.pow specifies that it raises x to the power of y.
So, math.pow calculates (-5)**4. whereas just writing -5**4 is equivalent to -1 * 5 **4, since by operator precedence in Python the unary operator - has less precedence than the power operator **.
This is because of the operator precedence in python.
If we look at the operator precedence, we see that the unary operator -x has lower precedence than the power operator x**y, so that the expression -5**2 means to first apply the square to the 5 and then apply the negative sign to the result. This first operation gives 25, which then gives -25.
The expression math.pow(-5,2) means to square the -5. This is equivalent to (-5)**2.
It's because of that the precedence of - operator is lower than power operator **. In fact your expression will be calculated as following:
>>> -(5 ** 2)
Try the following to get the correct result:
>>> (-5) ** 2
25
For more information you can read the python Operator precedence:
The following table summarizes the operator precedence in Python, from lowest precedence (least binding) to highest precedence (most binding).

Calculation error with pow operator

What should print (-2 ** 2) return? According to my calculations it should be 4, but interpreter returns -4.
Is this Python's thing or my math is that terrible?
According to docs, ** has higher precedence than -, thus your code is equivalent to -(2 ** 2). To get the desired result you could put -2 into parentheses
>>> (-2) ** 2
4
or use built-in pow function
>>> pow(-2, 2)
4
or math.pow function (returning float value)
>>> import math
>>> math.pow(-2, 2)
4.0
The ** operation is done before the minus. To get the results expected, you should do
print ((-2) ** 2)
From the documentation:
Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands): -1**2 results in -1.
A full detail of operators precedence is also available in the documentation. You can see the last line is (expr) which force the expr to be evaluated before being used, hence the result of (-2) ** 2 = 4
you can also use math library...
math.pow(-2,2) --> 4
-math.pow(2,2) --> -4
math.pow(4,0.5) --> 2
Python has a problem and does not see the -2 as a number. This seems to be by design as it is mentioned in the docs.
-2 is interpreted as -(2) {unary minus to positive number 2}
That usually doesn't give a problem but in -a ** 2 the ** has higher priority as - and so with - interpreted as a unary operatoe instead of part of the number -2 ** 2 evaluates to -2 instead of 2.

Categories