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:

1 2 | False and X |

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:

1 2 3 | if txt is not None and txt.startswith('https://abder.io'): print('Great! You entered the correct URL') |

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:

1 2 | print('https://abder.io') if txt is None print('') |

The new protocol would do the following (I will explain what’s happening in a moment):

1 2 3 4 5 6 7 8 9 10 11 | _cb = if txt is None _type_cb = type(cb) if _cb: _result = print('https://abder.io') if hassattr(_type_cb, '__then__'): _result = _type_cb.__then__(_cb, _result) else: _result = print('') if hasattr(_type_cb, '__else__'): _result = _type_cb.__else__(_cb, _result) |

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:

1 2 | print('https://abder.io') if txt is None print('') |

The right-associative circuit breaking operator would be as follows:

1 2 | print('https://abder.io') if print('') |

In this protocol, the statement would be:

1 2 3 | _cb = print('') _result = print('https://abder.io') if _cb else _cb |

On the other hand, the left-associative circuit operator which is written as follows:

1 2 | print('https://abder.io') else print('') |

could be written using the protocol as:

1 2 3 | _cb = print('https://abder.io') _result = _cb if _cb else print('') |

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:

1 2 3 4 | value = 1 counter = 0 result = counter and value / counter |

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`

:

1 2 | type(LHS).__then__(LHS) |

On the other hand, if RHS is True, the following statement will be called:

1 2 | type(LHS).__else__(RHS) |

This can be written as follows:

1 2 | result = type(LHS).__then__(LHS) if LHS else type(LHS).__else__(LHS, RHS) |

If we want to implement the above, we can do the following:

1 2 3 4 5 6 7 8 9 10 11 12 13 | class CircuitBreaker: def __bool__(self): # True or False? return booleanValue def __then__(self): # If True, return something return self def __else__(self, RHS): # If False, evaluate RHS return RHS |

## 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)`

For `or`

, it is:

`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:

1 2 3 | assert bool(A and B) == (not (not A or not B)) assert bool(A or B) == (not (not A and not B)) |

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:

1 2 3 | assert bool(B if A) == bool(not (not A else not B)) assert bool(A else B) == bool(not (not B if not A)) |

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