问题
I cannot produce example in Python which shows Boolean operator precedence rules combined with short circuit evaluation. I can show operator precedence using:
print(1 or 0 and 0) # Returns 1 because `or` is evaluated 2nd.
But the issue with short circuiting shows up when I change it to this:
def yay(): print('yay'); return True
def nay(): print('nay')
def nope(): print('nope')
print(yay() or nay() and nope()) # Prints "yay\nTrue"
For each of 4 possibilities when expression before or
is True
it is the only evaluated expression. If operator precedence works this should print "nay\nnope\nyay\nTrue"
or "nay\nyay\nTrue"
, with short circuiting, because and
should be evaluated 1st.
What comes to mind from this example is that Python reads boolean expression from left to right and ends it when result is known regardless of operator precedence.
Where is my error or what am I missing? Please give an example where it's visible that and
is evaluated 1st and it isn't due to code being interpreted from left to right.
回答1:
You are confusing operator precedence and evaluation order.
The expression r = x or y and z
is not evaluated as tmp = y and z; r = x or tmp
, but just as r = x or (y and z)
. This expression is evaluated from left to right, and if the result of the or
is already decided, then (y and z)
will not be evaluated at all.
Note that it would be different if or
and and
were functions; in this case, the parameters of the functions would be evaluated before the function itself is called. Hence, operator.or_(yay(), operator.and_(nay(), nope()))
prints yay
, nay
and nope
i.e. it prints all three, but still in order from left to right.
You can generalize this to other operators, too. The following two expressions will yield different results due to the different operator precedence (both implicit and explicit by using (...)
), but the functions are called from left to right both times.
>>> def f(x): print(x); return x
>>> f(1) + f(2) * f(3) / f(4) ** f(5) - f(6) # 1 2 3 4 5 6 -> -4.99
>>> (f(1) + f(2)) * (((f(3) / f(4)) ** f(5)) - f(6)) # 1 2 3 4 5 6 -> -17.29
As pointed out in comments, while the terms in between operations are evaluated from left to right, the actual operations are evaluated according to their precedence.
class F:
def __init__(self,x): self.x = x
def __add__(self, other): print(f"add({self},{other})"); return F(self.x+other.x)
def __mul__(self, other): print(f"mul({self},{other})"); return F(self.x*other.x)
def __pow__(self, other): print(f"pow({self},{other})"); return F(self.x**other.x)
def __repr__(self): return str(self.x)
def f(x): print(x); return F(x)
This way, the expression f(1) + f(2) ** f(3) * f(4)
is evaluated as 1
, 2
, 3
, pow(2,3)
, 4
, mul(8,4)
, add(1,32)
, i.e. terms are evaluated left-to-right (and pushed on a stack) and expressions are evaluated as soon as their parameters are evaluated.
回答2:
The first value returned from yay()
is True
so python will not even run the rest of the expression. This is for efficiency, as the rest of the expression will not impact the result.
Take the following example where I have changed the order:
def yay(): print('yay'); return True
def nay(): print('nay')
def nope(): print('nope')
print(nay() and nope() or yay())
Output:
nay
yay
True
What is happening here? nay()
returns None
which is falsy so we already know that it doesn't matter what nope()
returns (because it is an and
condition and the first part is already False
) - so we don't run it. We then run the yay()
because it could change the already False
value of the expression - which returns True
.
回答3:
Actually, your code returns 1
not because or
is evaluated 2nd but because 1 is true and no further evalution is necessary. Which makes the behaviour consistent.
回答4:
or
has lower precedence than and
so that a or b and c
is interpreted as a or (b and c)
.
In addition, the logical operators are evaluated "lazily" so that if a
is True
, a or b
will not cause the evaluation of b
.
回答5:
Python evaluates expression from left to right and stops as soon as the result is known. For ex, in case of an or operator, if the entity of left side is True then it is for sure that the operator will return true do the expression on right will not be evaluated in this case.
In case of and
operator, if the expression on the left side is False, it is sure that the operator should return False. So the expression to the right is not evaluated here.
This is what is happening in your example.
来源:https://stackoverflow.com/questions/51784005/python-or-and-operator-precedence-example