Re: PEP 335: Overloadable Boolean Operators - Official Posting

From: Colin J. Williams (cjw_at_sympatico.ca)
Date: 09/10/04


Date: Fri, 10 Sep 2004 08:11:47 -0400

I understand that the intent, eventually, is that the basic types become
classes.

Would making Bool a class permit the achievement of these objectives?

If Bool were a class, then subclasses could use __repr__ to provide
alternative responses to False/True, such as No/Yes or Fail/OK.

Colin W.

Greg Ewing wrote:

> PEP: 335
> Title: Overloadable Boolean Operators
> Version: $Revision: 1.2 $
> Last-Modified: $Date: 2004/09/09 14:17:17 $
> Author: Gregory Ewing <greg@cosc.canterbury.ac.nz>
> Status: Draft
> Type: Standards Track
> Content-Type: text/x-rst
> Created: 29-Aug-2004
> Python-Version: 2.4
> Post-History: 05-Sep-2004
>
>
> Abstract
> ========
>
> This PEP proposes an extension to permit objects to define their own
> meanings for the boolean operators 'and', 'or' and 'not', and suggests
> an efficient strategy for implementation. A prototype of this
> implementation is available for download.
>
>
> Background
> ==========
>
> Python does not currently provide any '__xxx__' special methods
> corresponding to the 'and', 'or' and 'not' boolean operators. In the
> case of 'and' and 'or', the most likely reason is that these operators
> have short-circuiting semantics, i.e. the second operand is not
> evaluated if the result can be determined from the first operand. The
> usual technique of providing special methods for these operators
> therefore would not work.
>
> There is no such difficulty in the case of 'not', however, and it
> would be straightforward to provide a special method for this
> operator. The rest of this proposal will therefore concentrate mainly
> on providing a way to overload 'and' and 'or'.
>
>
> Motivation
> ==========
>
> There are many applications in which it is natural to provide custom
> meanings for Python operators, and in some of these, having boolean
> operators excluded from those able to be customised can be
> inconvenient. Examples include:
>
> 1. Numeric/Numarray, in which almost all the operators are defined on
> arrays so as to perform the appropriate operation between
> corresponding elements, and return an array of the results. For
> consistency, one would expect a boolean operation between two
> arrays to return an array of booleans, but this is not currently
> possible.
>
> There is a precedent for an extension of this kind: comparison
> operators were originally restricted to returning boolean results,
> and rich comparisons were added so that comparisons of Numeric
> arrays could return arrays of booleans.
>
> 2. A symbolic algebra system, in which a Python expression is
> evaluated in an environment which results in it constructing a tree
> of objects corresponding to the structure of the expression.
>
> 3. A relational database interface, in which a Python expression is
> used to construct an SQL query.
>
> A workaround often suggested is to use the bitwise operators '&', '|'
> and '~' in place of 'and', 'or' and 'not', but this has some
> drawbacks. The precedence of these is different in relation to the
> other operators, and they may already be in use for other purposes (as
> in example 1). There is also the aesthetic consideration of forcing
> users to use something other than the most obvious syntax for what
> they are trying to express. This would be particularly acute in the
> case of example 3, considering that boolean operations are a staple of
> SQL queries.
>
>
> Rationale
> =========
>
> The requirements for a successful solution to the problem of allowing
> boolean operators to be customised are:
>
> 1. In the default case (where there is no customisation), the existing
> short-circuiting semantics must be preserved.
>
> 2. There must not be any appreciable loss of speed in the default
> case.
>
> 3. If possible, the customisation mechanism should allow the object to
> provide either short-circuiting or non-short-circuiting semantics,
> at its discretion.
>
> One obvious strategy, that has been previously suggested, is to pass
> into the special method the first argument and a function for
> evaluating the second argument. This would satisfy requirements 1 and
> 3, but not requirement 2, since it would incur the overhead of
> constructing a function object and possibly a Python function call on
> every boolean operation. Therefore, it will not be considered further
> here.
>
> The following section proposes a strategy that addresses all three
> requirements. A `prototype implementation`_ of this strategy is
> available for download.
>
> .. _prototype implementation:
> http://www.cosc.canterbury.ac.nz/~greg/python/obo//Python_OBO.tar.gz
>
>
> Specification
> =============
>
> Special Methods
> ---------------
>
> At the Python level, objects may define the following special methods.
>
> =============== ================= ========================
> Unary Binary, phase 1 Binary, phase 2
> =============== ================= ========================
> * __not__(self) * __and1__(self) * __and2__(self, other)
> * __or1__(self) * __or2__(self, other)
> * __rand2__(self, other)
> * __ror2__(self, other)
> =============== ================= ========================
>
> The __not__ method, if defined, implements the 'not' operator. If it
> is not defined, or it returns NotImplemented, existing semantics are
> used.
>
> To permit short-circuiting, processing of the 'and' and 'or' operators
> is split into two phases. Phase 1 occurs after evaluation of the first
> operand but before the second. If the first operand defines the
> appropriate phase 1 method, it is called with the first operand as
> argument. If that method can determine the result without needing the
> second operand, it returns the result, and further processing is
> skipped.
>
> If the phase 1 method determines that the second operand is needed, it
> returns the special value NeedOtherOperand. This triggers the
> evaluation of the second operand, and the calling of an appropriate
> phase 2 method. During phase 2, the __and2__/__rand2__ and
> __or2__/__ror2__ method pairs work as for other binary operators.
>
> Processing falls back to existing semantics if at any stage a relevant
> special method is not found or returns NotImplemented.
>
> As a special case, if the first operand defines a phase 2 method but
> no corresponding phase 1 method, the second operand is always
> evaluated and the phase 2 method called. This allows an object which
> does not want short-circuiting semantics to simply implement the
> relevant phase 2 methods and ignore phase 1.
>
>
> Bytecodes
> ---------
>
> The patch adds four new bytecodes, LOGICAL_AND_1, LOGICAL_AND_2,
> LOGICAL_OR_1 and LOGICAL_OR_2. As an example of their use, the
> bytecode generated for an 'and' expression looks like this::
>
> .
> .
> .
> evaluate first operand
> LOGICAL_AND_1 L
> evaluate second operand
> LOGICAL_AND_2
> L: .
> .
> .
>
> The LOGICAL_AND_1 bytecode performs phase 1 processing. If it
> determines that the second operand is needed, it leaves the first
> operand on the stack and continues with the following code. Otherwise
> it pops the first operand, pushes the result and branches to L.
>
> The LOGICAL_AND_2 bytecode performs phase 2 processing, popping both
> operands and pushing the result.
>
>
> Type Slots
> ----------
>
> A the C level, the new special methods are manifested as five new
> slots in the type object. In the patch, they are added to the
> tp_as_number substructure, since this allowed making use of some
> existing code for dealing with unary and binary operators. Their
> existence is signalled by a new type flag,
> Py_TPFLAGS_HAVE_BOOLEAN_OVERLOAD.
>
> The new type slots are::
>
> unaryfunc nb_logical_not;
> unaryfunc nb_logical_and_1;
> unaryfunc nb_logical_or_1;
> binaryfunc nb_logical_and_2;
> binaryfunc nb_logical_or_2;
>
>
> Python/C API Functions
> ----------------------
>
> There are also five new Python/C API functions corresponding to the
> new operations::
>
> PyObject *PyObject_LogicalNot(PyObject *);
> PyObject *PyObject_LogicalAnd1(PyObject *);
> PyObject *PyObject_LogicalOr1(PyObject *);
> PyObject *PyObject_LogicalAnd2(PyObject *, PyObject *);
> PyObject *PyObject_LogicalOr2(PyObject *, PyObject *);
>
>
> Copyright
> =========
>
> This document has been placed in the public domain.
>
>
> ..
> Local Variables:
> mode: indented-text
> indent-tabs-mode: nil
> sentence-end-double-space: t
> fill-column: 70
> End:
>



Relevant Pages

  • Re: PEP 335: Overloadable Boolean Operators - Official Posting
    ... > Title: Overloadable Boolean Operators ... > evaluated if the result can be determined from the first operand. ... > usual technique of providing special methods for these operators ...
    (comp.lang.python)
  • Proto-PEP: Overloadable Boolean Operators
    ... meanings for the boolean operators 'and', 'or' and 'not', and suggests ... evaluated if the result can be determined from the first operand. ... usual techique of providing special methods for these operators ... in which a Python expression is ...
    (comp.lang.python)
  • Re: floating point arithmetic
    ... "the quotient from the division of the first operand ... It doesn't directly apply to a binary operator. ...
    (comp.lang.c)
  • Re: Please explain nuances of ||=
    ... On Fri, 2 May 2008, Phillip Gawlowski wrote: ... Which is required for || and OR to use their *second* operand ... (since it otherwise uses the first operand), so x || x = y cannot work. ... All I'm saying is that h||= ...
    (comp.lang.ruby)
  • Re: Formatting a string for proper calculation
    ... You could use MODto determine if it's before the first operand or ... > which add another layer to properly add the parenthesis, ... >> Once you've got the array, you spin through it and reassemble the string ...
    (microsoft.public.fox.programmer.exchange)