Recently I read Kurt Rose's DISappearing and (via Planet Python), where Kurt noted that Python doesn't have
__...__
methods that let you override boolean
and
and
or
operations on your class objects. As it happens, there's a really good reason for this, which is that Python would require a new fundamental data type in order to make it really work.
Boolean
and
and
or
have the extremely valuable property of short-circuiting evaluation, where if you write, say, '
a() and
b()
' and
a()
evaluates to false, Python will not even call
b()
. Let's imagine a hypothetical world in which Python allows you to do this overriding and the boolean operators still preserve this short circuiting. As usual, if you write '
a and b
', this will (at least some of the time) translate into a call to the override method on
a
, let's call it
__band__
, and the
__band__
method will receive an additional argument that represents the right hand side:
class AClass:
def __band__(self, right):
....
Now here is the big question: what's the type of
right
in this method?
In binary
__and__
,
right
is the value we get from evaluating the right hand side expression; if you write '
a & b()
', this is roughly the same as
a.__and__(b())
. However this can't be the case for
__band__
, because that would mean no more short-circuiting; if
a
had a
__band__
method, writing
a and b()
would call
b()
all of the time. To preserve short-circuiting,
right
has to be some type that represents the right hand side expression in an un-evaluated form.
However, Python has no such type today. Closures sort of come close, but they create additional effects and do things like appear in Python exception backtraces. This means that adding override methods for boolean operations would require either discarding short-circuiting (and making
right
be the evaluation result) or figuring out and introducing a new, relatively complex type in Python just to support this.
( Continuations are sort of what you'd need but I think they're not quite what you want, or at least you need a continuation that captures only the right side expression.)
The other problem of such a
right
type is that you'd want to be able to peer inside it relatively easily. After all, the entire purpose of implementing your own
__band__
method is so that you can do something different from a plain boolean
and
when the right hand side is some special thing. If all you're going to do is:
def __band__(self, right):
if not bool(self):
return False
else:
return right.eval()
then there's not really any point in having a
__band__
at all, especially given the general complexity involved in Python as a whole.
(This is of course not necessarily the only reason for Python to fence off boolean operations as things that you absolutely can't override. You can certainly argue that they should be inviolate and not subject to clever redefinitions simply as a matter of principle.)