Circuit breaking operator and protocol
Let’s say that you were given an ugly boolean expression to evaluate. Assume in the following statement that the ugly part resides in X
, which can be thought of as a very large boolean expression that would take some time (i.e. seconds) to evaluate:
If we take a deeper look, we will notice that no matter what X
will evaluate to, the statement will always return False
.
Fortunately, Python uses a technique called short-circuit evaluation to speed up the process of such boolean expression. So, what will Python do in the above statement is it will notice that the result of the expression does not depend on X
at all, and will thus return False
immediately without evaluating X
. The above statement is called logical conjunction (i.e. and
).
Another example where short-circuit evaluation could be used is in the following example:
In this case, Python will evaluate the first part of the if-condition
. If it evaluates to False
, it won't bother moving to the second part since we will always get False
. This example is called a conditional expression.
Circuit breaking protocol
Nick Coghlan and Mark E. Haase proposed a new protocol (PEP 532) that mainly aims to enhance the way we build short-circuited expressions in a more elaborative way. It tries to give the left-hand operand access to a short-circuiting operation.
Let’s take this simple conditional expression and see how it would be interpreted using the new protocol:
The new protocol would do the following (I will explain what’s happening in a moment):
We can notice that the interpreter would only access the protocol method (i.e. __then__
) of the conditional expression branch that is actually executed, provided that the method look up would be via the circuit breaker's type.
The term circuit breaking is analogous to what is happening in the electrical domain, such that the same way circuit breakers in electrical systems detect and handle short circuits before they make any harm, circuit breakers in Python detect and handle short circuits in expressions before triggering any exceptions.
Circuit breaking operators IF and ELSE
There are two circuit breaking operators, the right-associative and the left-associative circuit breaking operators.
For the statement:
The right-associative circuit breaking operator would be as follows:
In this protocol, the statement would be:
On the other hand, the left-associative circuit operator which is written as follows:
could be written using the protocol as:
What we can notice from the above two cases is that when the circuit breaking expression would short-circuit, the condition expression would be the _result
, unless of course it is a circuit breaker.
It is also important to note that the natural output is the right-associative operator since the right operand is always evaluated first, and the left operand is not evaluated at all if the right operand was True
(i.e. in a boolean context).
An example on circuit breakers is operator.if_true
and operator.if_false
(notice that they are the logical inversions of each other).
Hooking into Python’s AND
and OR
boolean operators
The way Python’s and
and or
boolean operators currently short-circuit is that if the outcome could be determined from the left-operand, then there is no need to evaluate the right-hand operand.
So, the following expression won’t raise an exception even though the right-hand expression raises a ZeroDivisionError
exception if it was evaluated:
As mentioned at the beginning of this article, PEP 532
is trying to give the left-hand operand access to a short-circuiting operation. For instance, the else
operator this PEP proposes enables the left-hand operand to handle the outcome based on its truth value.
Assume that:
LHS (Left-Hand Side) is: print(https://abder.io)
RHS (Right-Hand Side) is: print('')
In the following expression:
print('https://abder.io') else print('')
LHS is given access to both LHS and RHS based on the value of bool(LHS)
.
What will happen is the PEP will use the introduced __then__
and __else__
methods in such a way that the following statement will be called if LHS is considered True
:
On the other hand, if RHS is True, the following statement will be called:
This can be written as follows:
If we want to implement the above, we can do the following:
De Morgan’s laws
De Morgan’s laws define the interpretation of expressions involving the boolean operators: and
, or
, not
.
De Morgan’s law for and
is:
not (a and b) == (not a or not b)
not (a or b) == (not a and not b)
De Morgan’s laws are mentioned here since the short-circuiting operators allow multiple ways to write the same expression. Thus, through De Morgan’s laws we are able to express the results of and
and or
in terms of each other, in addition to a suitable combination of not
operations.
In terms of Python, and
and or
can be written as follows:
Remember that assert
in Python simply evaluates the expression and takes no action if the expression succeeds, otherwise it will raise an AssertionError
exception.
This also holds to the short-circuiting operators, such that:
Conclusion
The circuit breaking protocol enhances the way we build short-circuited expressions, as it tries to give the left-hand operand access to a short-circuiting operation. The protocol allows us to hook into Python’s and
and or
operators by allowing the left-hand operand to handle the outcome based on the truth value.
Originally published at https://abder.io on December 15, 2016.