CINXE.COM
PEP 335 – Overloadable Boolean Operators | peps.python.org
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 335 – Overloadable Boolean Operators | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0335/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 335 – Overloadable Boolean Operators | peps.python.org'> <meta property="og:description" content="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."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0335/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="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."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li> <li><a href="../pep-0000/">PEP Index</a> » </li> <li>PEP 335</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 335 – Overloadable Boolean Operators</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Gregory Ewing <greg.ewing at canterbury.ac.nz></dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Created<span class="colon">:</span></dt> <dd class="field-even">29-Aug-2004</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.3</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even">05-Sep-2004, 30-Sep-2011, 25-Oct-2011</dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#rejection-notice">Rejection Notice</a></li> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#background">Background</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#special-methods">Special Methods</a></li> <li><a class="reference internal" href="#bytecodes">Bytecodes</a></li> <li><a class="reference internal" href="#type-slots">Type Slots</a></li> <li><a class="reference internal" href="#python-c-api-functions">Python/C API Functions</a></li> </ul> </li> <li><a class="reference internal" href="#alternatives-and-optimisations">Alternatives and Optimisations</a><ul> <li><a class="reference internal" href="#reduced-special-method-set">Reduced special method set</a></li> <li><a class="reference internal" href="#additional-bytecodes">Additional bytecodes</a></li> <li><a class="reference internal" href="#optimisation-of-not">Optimisation of ‘not’</a></li> </ul> </li> <li><a class="reference internal" href="#usage-examples">Usage Examples</a><ul> <li><a class="reference internal" href="#example-1-numpy-arrays">Example 1: NumPy Arrays</a></li> <li><a class="reference internal" href="#example-1-output">Example 1 Output</a></li> <li><a class="reference internal" href="#example-2-database-queries">Example 2: Database Queries</a></li> <li><a class="reference internal" href="#example-2-output">Example 2 Output</a></li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="rejection-notice"> <h2><a class="toc-backref" href="#rejection-notice" role="doc-backlink">Rejection Notice</a></h2> <p>This PEP was rejected. See <a class="reference external" href="https://mail.python.org/pipermail/python-dev/2012-March/117510.html">https://mail.python.org/pipermail/python-dev/2012-March/117510.html</a></p> </section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>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.</p> </section> <section id="background"> <h2><a class="toc-backref" href="#background" role="doc-backlink">Background</a></h2> <p>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.</p> <p>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’.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>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:</p> <ol class="arabic"> <li>NumPy, 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.<p>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 NumPy arrays could return arrays of booleans.</p> </li> <li>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.</li> <li>A relational database interface, in which a Python expression is used to construct an SQL query.</li> </ol> <p>A workaround often suggested is to use the bitwise operators ‘&’, ‘|’ and ‘~’ in place of ‘and’, ‘or’ and ‘not’, but this has some drawbacks:</p> <ul class="simple"> <li>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).</li> <li>It is aesthetically displeasing to force 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.</li> <li>Bitwise operators do not provide a solution to the problem of chained comparisons such as ‘a < b < c’ which involve an implicit ‘and’ operation. Such expressions currently cannot be used at all on data types such as NumPy arrays where the result of a comparison cannot be treated as having normal boolean semantics; they must be expanded into something like (a < b) & (b < c), losing a considerable amount of clarity.</li> </ul> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>The requirements for a successful solution to the problem of allowing boolean operators to be customised are:</p> <ol class="arabic simple"> <li>In the default case (where there is no customisation), the existing short-circuiting semantics must be preserved.</li> <li>There must not be any appreciable loss of speed in the default case.</li> <li>Ideally, the customisation mechanism should allow the object to provide either short-circuiting or non-short-circuiting semantics, at its discretion.</li> </ol> <p>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.</p> <p>The following section proposes a strategy that addresses all three requirements. A <a class="reference external" href="http://www.cosc.canterbury.ac.nz/~greg/python/obo//Python_OBO.tar.gz">prototype implementation</a> of this strategy is available for download.</p> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <section id="special-methods"> <h3><a class="toc-backref" href="#special-methods" role="doc-backlink">Special Methods</a></h3> <p>At the Python level, objects may define the following special methods.</p> <table class="docutils align-default"> <thead> <tr class="row-odd"><th class="head">Unary</th> <th class="head">Binary, phase 1</th> <th class="head">Binary, phase 2</th> </tr> </thead> <tbody> <tr class="row-even"><td><ul class="simple"> <li>__not__(self)</li> </ul> </td> <td><ul class="simple"> <li>__and1__(self)</li> <li>__or1__(self)</li> </ul> </td> <td><ul class="simple"> <li>__and2__(self, other)</li> <li>__or2__(self, other)</li> <li>__rand2__(self, other)</li> <li>__ror2__(self, other)</li> </ul> </td> </tr> </tbody> </table> <p>The __not__ method, if defined, implements the ‘not’ operator. If it is not defined, or it returns NotImplemented, existing semantics are used.</p> <p>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 relevant 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.</p> <p>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 a relevant phase 2 method. During phase 2, the __and2__/__rand2__ and __or2__/__ror2__ method pairs work as for other binary operators.</p> <p>Processing falls back to existing semantics if at any stage a relevant special method is not found or returns NotImplemented.</p> <p>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 phase 2 methods and ignore phase 1.</p> </section> <section id="bytecodes"> <h3><a class="toc-backref" href="#bytecodes" role="doc-backlink">Bytecodes</a></h3> <p>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:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="o">.</span> <span class="o">.</span> <span class="o">.</span> <span class="n">evaluate</span> <span class="n">first</span> <span class="n">operand</span> <span class="n">LOGICAL_AND_1</span> <span class="n">L</span> <span class="n">evaluate</span> <span class="n">second</span> <span class="n">operand</span> <span class="n">LOGICAL_AND_2</span> <span class="n">L</span><span class="p">:</span> <span class="o">.</span> <span class="o">.</span> <span class="o">.</span> </pre></div> </div> <p>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.</p> <p>The LOGICAL_AND_2 bytecode performs phase 2 processing, popping both operands and pushing the result.</p> </section> <section id="type-slots"> <h3><a class="toc-backref" href="#type-slots" role="doc-backlink">Type Slots</a></h3> <p>At 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 allows 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.</p> <p>The new type slots are:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">unaryfunc</span> <span class="n">nb_logical_not</span><span class="p">;</span> <span class="n">unaryfunc</span> <span class="n">nb_logical_and_1</span><span class="p">;</span> <span class="n">unaryfunc</span> <span class="n">nb_logical_or_1</span><span class="p">;</span> <span class="n">binaryfunc</span> <span class="n">nb_logical_and_2</span><span class="p">;</span> <span class="n">binaryfunc</span> <span class="n">nb_logical_or_2</span><span class="p">;</span> </pre></div> </div> </section> <section id="python-c-api-functions"> <h3><a class="toc-backref" href="#python-c-api-functions" role="doc-backlink">Python/C API Functions</a></h3> <p>There are also five new Python/C API functions corresponding to the new operations:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span> <span class="o">*</span><span class="n">PyObject_LogicalNot</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">);</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">PyObject_LogicalAnd1</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">);</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">PyObject_LogicalOr1</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">);</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">PyObject_LogicalAnd2</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="p">);</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">PyObject_LogicalOr2</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="p">);</span> </pre></div> </div> </section> </section> <section id="alternatives-and-optimisations"> <h2><a class="toc-backref" href="#alternatives-and-optimisations" role="doc-backlink">Alternatives and Optimisations</a></h2> <p>This section discusses some possible variations on the proposal, and ways in which the bytecode sequences generated for boolean expressions could be optimised.</p> <section id="reduced-special-method-set"> <h3><a class="toc-backref" href="#reduced-special-method-set" role="doc-backlink">Reduced special method set</a></h3> <p>For completeness, the full version of this proposal includes a mechanism for types to define their own customised short-circuiting behaviour. However, the full mechanism is not needed to address the main use cases put forward here, and it would be possible to define a simplified version that only includes the phase 2 methods. There would then only be 5 new special methods (__and2__, __rand2__, __or2__, __ror2__, __not__) with 3 associated type slots and 3 API functions.</p> <p>This simplified version could be expanded to the full version later if desired.</p> </section> <section id="additional-bytecodes"> <h3><a class="toc-backref" href="#additional-bytecodes" role="doc-backlink">Additional bytecodes</a></h3> <p>As defined here, the bytecode sequence for code that branches on the result of a boolean expression would be slightly longer than it currently is. For example, in Python 2.7,</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">a</span> <span class="ow">and</span> <span class="n">b</span><span class="p">:</span> <span class="n">statement1</span> <span class="k">else</span><span class="p">:</span> <span class="n">statement2</span> </pre></div> </div> <p>generates</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="n">LOAD_GLOBAL</span> <span class="n">a</span> <span class="n">POP_JUMP_IF_FALSE</span> <span class="n">false_branch</span> <span class="n">LOAD_GLOBAL</span> <span class="n">b</span> <span class="n">POP_JUMP_IF_FALSE</span> <span class="n">false_branch</span> <span class="o"><</span><span class="n">code</span> <span class="k">for</span> <span class="n">statement1</span><span class="o">></span> <span class="n">JUMP_FORWARD</span> <span class="n">end_branch</span> <span class="n">false_branch</span><span class="p">:</span> <span class="o"><</span><span class="n">code</span> <span class="k">for</span> <span class="n">statement2</span><span class="o">></span> <span class="n">end_branch</span><span class="p">:</span> </pre></div> </div> <p>Under this proposal as described so far, it would become something like</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="n">LOAD_GLOBAL</span> <span class="n">a</span> <span class="n">LOGICAL_AND_1</span> <span class="n">test</span> <span class="n">LOAD_GLOBAL</span> <span class="n">b</span> <span class="n">LOGICAL_AND_2</span> <span class="n">test</span><span class="p">:</span> <span class="n">POP_JUMP_IF_FALSE</span> <span class="n">false_branch</span> <span class="o"><</span><span class="n">code</span> <span class="k">for</span> <span class="n">statement1</span><span class="o">></span> <span class="n">JUMP_FORWARD</span> <span class="n">end_branch</span> <span class="n">false_branch</span><span class="p">:</span> <span class="o"><</span><span class="n">code</span> <span class="k">for</span> <span class="n">statement2</span><span class="o">></span> <span class="n">end_branch</span><span class="p">:</span> </pre></div> </div> <p>This involves executing one extra bytecode in the short-circuiting case and two extra bytecodes in the non-short-circuiting case.</p> <p>However, by introducing extra bytecodes that combine the logical operations with testing and branching on the result, it can be reduced to the same number of bytecodes as the original:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="n">LOAD_GLOBAL</span> <span class="n">a</span> <span class="n">AND1_JUMP</span> <span class="n">true_branch</span><span class="p">,</span> <span class="n">false_branch</span> <span class="n">LOAD_GLOBAL</span> <span class="n">b</span> <span class="n">AND2_JUMP_IF_FALSE</span> <span class="n">false_branch</span> <span class="n">true_branch</span><span class="p">:</span> <span class="o"><</span><span class="n">code</span> <span class="k">for</span> <span class="n">statement1</span><span class="o">></span> <span class="n">JUMP_FORWARD</span> <span class="n">end_branch</span> <span class="n">false_branch</span><span class="p">:</span> <span class="o"><</span><span class="n">code</span> <span class="k">for</span> <span class="n">statement2</span><span class="o">></span> <span class="n">end_branch</span><span class="p">:</span> </pre></div> </div> <p>Here, AND1_JUMP performs phase 1 processing as above, and then examines the result. If there is a result, it is popped from the stack, its truth value is tested and a branch taken to one of two locations.</p> <p>Otherwise, the first operand is left on the stack and execution continues to the next bytecode. The AND2_JUMP_IF_FALSE bytecode performs phase 2 processing, pops the result and branches if it tests false</p> <p>For the ‘or’ operator, there would be corresponding OR1_JUMP and OR2_JUMP_IF_TRUE bytecodes.</p> <p>If the simplified version without phase 1 methods is used, then early exiting can only occur if the first operand is false for ‘and’ and true for ‘or’. Consequently, the two-target AND1_JUMP and OR1_JUMP bytecodes can be replaced with AND1_JUMP_IF_FALSE and OR1_JUMP_IF_TRUE, these being ordinary branch instructions with only one target.</p> </section> <section id="optimisation-of-not"> <h3><a class="toc-backref" href="#optimisation-of-not" role="doc-backlink">Optimisation of ‘not’</a></h3> <p>Recent versions of Python implement a simple optimisation in which branching on a negated boolean expression is implemented by reversing the sense of the branch, saving a UNARY_NOT opcode.</p> <p>Taking a strict view, this optimisation should no longer be performed, because the ‘not’ operator may be overridden to produce quite different results from usual. However, in typical use cases, it is not envisaged that expressions involving customised boolean operations will be used for branching – it is much more likely that the result will be used in some other way.</p> <p>Therefore, it would probably do little harm to specify that the compiler is allowed to use the laws of boolean algebra to simplify any expression that appears directly in a boolean context. If this is inconvenient, the result can always be assigned to a temporary name first.</p> <p>This would allow the existing ‘not’ optimisation to remain, and would permit future extensions of it such as using De Morgan’s laws to extend it deeper into the expression.</p> </section> </section> <section id="usage-examples"> <h2><a class="toc-backref" href="#usage-examples" role="doc-backlink">Usage Examples</a></h2> <section id="example-1-numpy-arrays"> <h3><a class="toc-backref" href="#example-1-numpy-arrays" role="doc-backlink">Example 1: NumPy Arrays</a></h3> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">#-----------------------------------------------------------------</span> <span class="c1">#</span> <span class="c1"># This example creates a subclass of numpy array to which</span> <span class="c1"># 'and', 'or' and 'not' can be applied, producing an array</span> <span class="c1"># of booleans.</span> <span class="c1">#</span> <span class="c1">#-----------------------------------------------------------------</span> <span class="kn">from</span> <span class="nn">numpy</span> <span class="kn">import</span> <span class="n">array</span><span class="p">,</span> <span class="n">ndarray</span> <span class="k">class</span> <span class="nc">BArray</span><span class="p">(</span><span class="n">ndarray</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">"barray(</span><span class="si">%s</span><span class="s2">)"</span> <span class="o">%</span> <span class="n">ndarray</span><span class="o">.</span><span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__and2__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span> <span class="o">&</span> <span class="n">other</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__or2__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span> <span class="o">&</span> <span class="n">other</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__not__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">def</span> <span class="nf">barray</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">):</span> <span class="k">return</span> <span class="n">array</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">)</span><span class="o">.</span><span class="n">view</span><span class="p">(</span><span class="nb">type</span> <span class="o">=</span> <span class="n">BArray</span><span class="p">)</span> <span class="n">a0</span> <span class="o">=</span> <span class="n">barray</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="n">a1</span> <span class="o">=</span> <span class="n">barray</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="n">a2</span> <span class="o">=</span> <span class="n">barray</span><span class="p">([</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="n">a3</span> <span class="o">=</span> <span class="n">barray</span><span class="p">([</span><span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="nb">print</span> <span class="s2">"a0:"</span><span class="p">,</span> <span class="n">a0</span> <span class="nb">print</span> <span class="s2">"a1:"</span><span class="p">,</span> <span class="n">a1</span> <span class="nb">print</span> <span class="s2">"a2:"</span><span class="p">,</span> <span class="n">a2</span> <span class="nb">print</span> <span class="s2">"a3:"</span><span class="p">,</span> <span class="n">a3</span> <span class="nb">print</span> <span class="s2">"not a0:"</span><span class="p">,</span> <span class="ow">not</span> <span class="n">a0</span> <span class="nb">print</span> <span class="s2">"a0 == a1 and a2 == a3:"</span><span class="p">,</span> <span class="n">a0</span> <span class="o">==</span> <span class="n">a1</span> <span class="ow">and</span> <span class="n">a2</span> <span class="o">==</span> <span class="n">a3</span> <span class="nb">print</span> <span class="s2">"a0 == a1 or a2 == a3:"</span><span class="p">,</span> <span class="n">a0</span> <span class="o">==</span> <span class="n">a1</span> <span class="ow">or</span> <span class="n">a2</span> <span class="o">==</span> <span class="n">a3</span> </pre></div> </div> </section> <section id="example-1-output"> <h3><a class="toc-backref" href="#example-1-output" role="doc-backlink">Example 1 Output</a></h3> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a0</span><span class="p">:</span> <span class="n">barray</span><span class="p">([</span><span class="mi">0</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">4</span><span class="p">])</span> <span class="n">a1</span><span class="p">:</span> <span class="n">barray</span><span class="p">([</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">])</span> <span class="n">a2</span><span class="p">:</span> <span class="n">barray</span><span class="p">([</span><span class="mi">5</span> <span class="mi">6</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">])</span> <span class="n">a3</span><span class="p">:</span> <span class="n">barray</span><span class="p">([</span><span class="mi">5</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">4</span><span class="p">])</span> <span class="ow">not</span> <span class="n">a0</span><span class="p">:</span> <span class="n">barray</span><span class="p">([</span> <span class="kc">True</span> <span class="kc">False</span> <span class="kc">False</span> <span class="kc">False</span><span class="p">])</span> <span class="n">a0</span> <span class="o">==</span> <span class="n">a1</span> <span class="ow">and</span> <span class="n">a2</span> <span class="o">==</span> <span class="n">a3</span><span class="p">:</span> <span class="n">barray</span><span class="p">([</span><span class="kc">False</span> <span class="kc">False</span> <span class="kc">False</span> <span class="kc">True</span><span class="p">])</span> <span class="n">a0</span> <span class="o">==</span> <span class="n">a1</span> <span class="ow">or</span> <span class="n">a2</span> <span class="o">==</span> <span class="n">a3</span><span class="p">:</span> <span class="n">barray</span><span class="p">([</span><span class="kc">False</span> <span class="kc">False</span> <span class="kc">False</span> <span class="kc">True</span><span class="p">])</span> </pre></div> </div> </section> <section id="example-2-database-queries"> <h3><a class="toc-backref" href="#example-2-database-queries" role="doc-backlink">Example 2: Database Queries</a></h3> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">#-----------------------------------------------------------------</span> <span class="c1">#</span> <span class="c1"># This example demonstrates the creation of a DSL for database</span> <span class="c1"># queries allowing 'and' and 'or' operators to be used to</span> <span class="c1"># formulate the query.</span> <span class="c1">#</span> <span class="c1">#-----------------------------------------------------------------</span> <span class="k">class</span> <span class="nc">SQLNode</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__and2__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="k">return</span> <span class="n">SQLBinop</span><span class="p">(</span><span class="s2">"and"</span><span class="p">,</span> <span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__rand2__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="k">return</span> <span class="n">SQLBinop</span><span class="p">(</span><span class="s2">"and"</span><span class="p">,</span> <span class="n">other</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="k">return</span> <span class="n">SQLBinop</span><span class="p">(</span><span class="s2">"="</span><span class="p">,</span> <span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Table</span><span class="p">(</span><span class="n">SQLNode</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__tablename__</span> <span class="o">=</span> <span class="n">name</span> <span class="k">def</span> <span class="fm">__getattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span> <span class="k">return</span> <span class="n">SQLAttr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__sql__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__tablename__</span> <span class="k">class</span> <span class="nc">SQLBinop</span><span class="p">(</span><span class="n">SQLNode</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">opnd1</span><span class="p">,</span> <span class="n">opnd2</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">op</span> <span class="o">=</span> <span class="n">op</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">opnd1</span> <span class="o">=</span> <span class="n">opnd1</span> <span class="bp">self</span><span class="o">.</span><span class="n">opnd2</span> <span class="o">=</span> <span class="n">opnd2</span> <span class="k">def</span> <span class="nf">__sql__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">"(</span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2">)"</span> <span class="o">%</span> <span class="p">(</span><span class="n">sql</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">opnd1</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">op</span><span class="p">,</span> <span class="n">sql</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">opnd2</span><span class="p">))</span> <span class="k">class</span> <span class="nc">SQLAttr</span><span class="p">(</span><span class="n">SQLNode</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">table</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">table</span> <span class="o">=</span> <span class="n">table</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">def</span> <span class="nf">__sql__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">"</span><span class="si">%s</span><span class="s2">.</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">sql</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">table</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="k">class</span> <span class="nc">SQLSelect</span><span class="p">(</span><span class="n">SQLNode</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">targets</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">targets</span> <span class="o">=</span> <span class="n">targets</span> <span class="bp">self</span><span class="o">.</span><span class="n">where_clause</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">def</span> <span class="nf">where</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">expr</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">where_clause</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">return</span> <span class="bp">self</span> <span class="k">def</span> <span class="nf">__sql__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">result</span> <span class="o">=</span> <span class="s2">"SELECT </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">sql</span><span class="p">(</span><span class="n">target</span><span class="p">)</span> <span class="k">for</span> <span class="n">target</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">targets</span><span class="p">])</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">where_clause</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="s2">"</span><span class="si">%s</span><span class="s2"> WHERE </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">sql</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">where_clause</span><span class="p">))</span> <span class="k">return</span> <span class="n">result</span> <span class="k">def</span> <span class="nf">sql</span><span class="p">(</span><span class="n">expr</span><span class="p">):</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">expr</span><span class="p">,</span> <span class="n">SQLNode</span><span class="p">):</span> <span class="k">return</span> <span class="n">expr</span><span class="o">.</span><span class="n">__sql__</span><span class="p">()</span> <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">expr</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span> <span class="k">return</span> <span class="s2">"'</span><span class="si">%s</span><span class="s2">'"</span> <span class="o">%</span> <span class="n">expr</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"'"</span><span class="p">,</span> <span class="s2">"''"</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">expr</span><span class="p">)</span> <span class="k">def</span> <span class="nf">select</span><span class="p">(</span><span class="o">*</span><span class="n">targets</span><span class="p">):</span> <span class="k">return</span> <span class="n">SQLSelect</span><span class="p">(</span><span class="n">targets</span><span class="p">)</span> <span class="c1">#-----------------------------------------------------------------</span> <span class="n">dishes</span> <span class="o">=</span> <span class="n">Table</span><span class="p">(</span><span class="s2">"dishes"</span><span class="p">)</span> <span class="n">customers</span> <span class="o">=</span> <span class="n">Table</span><span class="p">(</span><span class="s2">"customers"</span><span class="p">)</span> <span class="n">orders</span> <span class="o">=</span> <span class="n">Table</span><span class="p">(</span><span class="s2">"orders"</span><span class="p">)</span> <span class="n">query</span> <span class="o">=</span> <span class="n">select</span><span class="p">(</span><span class="n">customers</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">dishes</span><span class="o">.</span><span class="n">price</span><span class="p">,</span> <span class="n">orders</span><span class="o">.</span><span class="n">amount</span><span class="p">)</span><span class="o">.</span><span class="n">where</span><span class="p">(</span> <span class="n">customers</span><span class="o">.</span><span class="n">cust_id</span> <span class="o">==</span> <span class="n">orders</span><span class="o">.</span><span class="n">cust_id</span> <span class="ow">and</span> <span class="n">orders</span><span class="o">.</span><span class="n">dish_id</span> <span class="o">==</span> <span class="n">dishes</span><span class="o">.</span><span class="n">dish_id</span> <span class="ow">and</span> <span class="n">dishes</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"Spam, Eggs, Sausages and Spam"</span><span class="p">)</span> <span class="nb">print</span> <span class="nb">repr</span><span class="p">(</span><span class="n">query</span><span class="p">)</span> <span class="nb">print</span> <span class="n">sql</span><span class="p">(</span><span class="n">query</span><span class="p">)</span> </pre></div> </div> </section> <section id="example-2-output"> <h3><a class="toc-backref" href="#example-2-output" role="doc-backlink">Example 2 Output</a></h3> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o"><</span><span class="n">__main__</span><span class="o">.</span><span class="n">SQLSelect</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x1cc830</span><span class="o">></span> <span class="n">SELECT</span> <span class="n">customers</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">dishes</span><span class="o">.</span><span class="n">price</span><span class="p">,</span> <span class="n">orders</span><span class="o">.</span><span class="n">amount</span> <span class="n">WHERE</span> <span class="p">(((</span><span class="n">customers</span><span class="o">.</span><span class="n">cust_id</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">cust_id</span><span class="p">)</span> <span class="n">AND</span> <span class="p">(</span><span class="n">orders</span><span class="o">.</span><span class="n">dish_id</span> <span class="o">=</span> <span class="n">dishes</span><span class="o">.</span><span class="n">dish_id</span><span class="p">))</span> <span class="n">AND</span> <span class="p">(</span><span class="n">dishes</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s1">'Spam, Eggs, Sausages and Spam'</span><span class="p">))</span> </pre></div> </div> </section> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0335.rst">https://github.com/python/peps/blob/main/peps/pep-0335.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0335.rst">2023-09-09 17:39:29 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#rejection-notice">Rejection Notice</a></li> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#background">Background</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#special-methods">Special Methods</a></li> <li><a class="reference internal" href="#bytecodes">Bytecodes</a></li> <li><a class="reference internal" href="#type-slots">Type Slots</a></li> <li><a class="reference internal" href="#python-c-api-functions">Python/C API Functions</a></li> </ul> </li> <li><a class="reference internal" href="#alternatives-and-optimisations">Alternatives and Optimisations</a><ul> <li><a class="reference internal" href="#reduced-special-method-set">Reduced special method set</a></li> <li><a class="reference internal" href="#additional-bytecodes">Additional bytecodes</a></li> <li><a class="reference internal" href="#optimisation-of-not">Optimisation of ‘not’</a></li> </ul> </li> <li><a class="reference internal" href="#usage-examples">Usage Examples</a><ul> <li><a class="reference internal" href="#example-1-numpy-arrays">Example 1: NumPy Arrays</a></li> <li><a class="reference internal" href="#example-1-output">Example 1 Output</a></li> <li><a class="reference internal" href="#example-2-database-queries">Example 2: Database Queries</a></li> <li><a class="reference internal" href="#example-2-output">Example 2 Output</a></li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0335.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>