[Python-checkins] peps: Updated PEP 335, posted 25-Oct-2011.
guido.van.rossum
python-checkins at python.org
Tue Oct 25 23:36:37 CEST 2011
http://hg.python.org/peps/rev/e197b2e8e14c
changeset: 3971:e197b2e8e14c
user: Guido van Rossum <guido at google.com>
date: Tue Oct 25 14:36:29 2011 -0700
summary:
Updated PEP 335, posted 25-Oct-2011.
files:
pep-0335.txt | 388 +++++++++++++++++++-------------------
1 files changed, 198 insertions(+), 190 deletions(-)
diff --git a/pep-0335.txt b/pep-0335.txt
--- a/pep-0335.txt
+++ b/pep-0335.txt
@@ -2,13 +2,13 @@
Title: Overloadable Boolean Operators
Version: $Revision$
Last-Modified: $Date$
-Author: Gregory Ewing <greg.ewing at canterbury.ac.nz>
+Author: Gregory Ewing <greg at cosc.canterbury.ac.nz>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 29-Aug-2004
Python-Version: 3.3
-Post-History: 05-Sep-2004, 30-Sep-2011
+Post-History: 05-Sep-2004, 30-Sep-2011, 25-Oct-2011
Abstract
@@ -66,13 +66,23 @@
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.
+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).
+
+* 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.
+
+* 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.
Rationale
@@ -208,7 +218,7 @@
PyObject *PyObject_LogicalAnd1(PyObject *);
PyObject *PyObject_LogicalOr1(PyObject *);
PyObject *PyObject_LogicalAnd2(PyObject *, PyObject *);
-
+ PyObject *PyObject_LogicalOr2(PyObject *, PyObject *);
Alternatives and Optimisations
@@ -242,40 +252,40 @@
::
- if a and b:
- statement1
- else:
- statement2
+ if a and b:
+ statement1
+ else:
+ statement2
generates
::
- LOAD_GLOBAL a
- POP_JUMP_IF_FALSE false_branch
- LOAD_GLOBAL b
- POP_JUMP_IF_FALSE false_branch
- <code for statement1>
- JUMP_FORWARD end_branch
- false_branch:
- <code for statement2>
- end_branch:
+ LOAD_GLOBAL a
+ POP_JUMP_IF_FALSE false_branch
+ LOAD_GLOBAL b
+ POP_JUMP_IF_FALSE false_branch
+ <code for statement1>
+ JUMP_FORWARD end_branch
+ false_branch:
+ <code for statement2>
+ end_branch:
Under this proposal as described so far, it would become something like
::
- LOAD_GLOBAL a
- LOGICAL_AND_1 test
- LOAD_GLOBAL b
- LOGICAL_AND_2
- test:
- POP_JUMP_IF_FALSE false_branch
- <code for statement1>
- JUMP_FORWARD end_branch
- false_branch:
- <code for statement2>
- end_branch:
+ LOAD_GLOBAL a
+ LOGICAL_AND_1 test
+ LOAD_GLOBAL b
+ LOGICAL_AND_2
+ test:
+ POP_JUMP_IF_FALSE false_branch
+ <code for statement1>
+ JUMP_FORWARD end_branch
+ false_branch:
+ <code for statement2>
+ end_branch:
This involves executing one extra bytecode in the short-circuiting
case and two extra bytecodes in the non-short-circuiting case.
@@ -286,16 +296,16 @@
::
- LOAD_GLOBAL a
- AND1_JUMP true_branch, false_branch
- LOAD_GLOBAL b
- AND2_JUMP_IF_FALSE false_branch
- true_branch:
- <code for statement1>
- JUMP_FORWARD end_branch
- false_branch:
- <code for statement2>
- end_branch:
+ LOAD_GLOBAL a
+ AND1_JUMP true_branch, false_branch
+ LOAD_GLOBAL b
+ AND2_JUMP_IF_FALSE false_branch
+ true_branch:
+ <code for statement1>
+ JUMP_FORWARD end_branch
+ false_branch:
+ <code for statement2>
+ end_branch:
Here, AND1_JUMP performs phase 1 processing as above,
and then examines the result. If there is a result, it is popped
@@ -350,58 +360,58 @@
::
- #-----------------------------------------------------------------
- #
- # This example creates a subclass of numpy array to which
- # 'and', 'or' and 'not' can be applied, producing an array
- # of booleans.
- #
- #-----------------------------------------------------------------
-
- from numpy import array, ndarray
-
- class BArray(ndarray):
-
- def __str__(self):
- return "barray(%s)" % ndarray.__str__(self)
-
- def __and2__(self, other):
- return self & other
-
- def __or2__(self, other):
- return self & other
-
- def __not__(self):
- return self == 0
-
- def barray(*args, **kwds):
- return array(*args, **kwds).view(type=BArray)
-
- a0 = barray([0, 1, 2, 4])
- a1 = barray([1, 2, 3, 4])
- a2 = barray([5, 6, 3, 4])
- a3 = barray([5, 1, 2, 4])
-
- print("a0:", a0)
- print("a1:", a1)
- print("a2:", a2)
- print("a3:", a3)
- print("not a0:", not a0)
- print("a0 == a1 and a2 == a3:", a0 == a1 and a2 == a3)
- print("a0 == a1 or a2 == a3:", a0 == a1 or a2 == a3)
+ #-----------------------------------------------------------------
+ #
+ # This example creates a subclass of numpy array to which
+ # 'and', 'or' and 'not' can be applied, producing an array
+ # of booleans.
+ #
+ #-----------------------------------------------------------------
+
+ from numpy import array, ndarray
+
+ class BArray(ndarray):
+
+ def __str__(self):
+ return "barray(%s)" % ndarray.__str__(self)
+
+ def __and2__(self, other):
+ return (self & other)
+
+ def __or2__(self, other):
+ return (self & other)
+
+ def __not__(self):
+ return (self == 0)
+
+ def barray(*args, **kwds):
+ return array(*args, **kwds).view(type = BArray)
+
+ a0 = barray([0, 1, 2, 4])
+ a1 = barray([1, 2, 3, 4])
+ a2 = barray([5, 6, 3, 4])
+ a3 = barray([5, 1, 2, 4])
+
+ print "a0:", a0
+ print "a1:", a1
+ print "a2:", a2
+ print "a3:", a3
+ print "not a0:", not a0
+ print "a0 == a1 and a2 == a3:", a0 == a1 and a2 == a3
+ print "a0 == a1 or a2 == a3:", a0 == a1 or a2 == a3
Example 1 Output
----------------
::
- a0: barray([0 1 2 4])
- a1: barray([1 2 3 4])
- a2: barray([5 6 3 4])
- a3: barray([5 1 2 4])
- not a0: barray([ True False False False])
- a0 == a1 and a2 == a3: barray([False False False True])
- a0 == a1 or a2 == a3: barray([False False False True])
+ a0: barray([0 1 2 4])
+ a1: barray([1 2 3 4])
+ a2: barray([5 6 3 4])
+ a3: barray([5 1 2 4])
+ not a0: barray([ True False False False])
+ a0 == a1 and a2 == a3: barray([False False False True])
+ a0 == a1 or a2 == a3: barray([False False False True])
Example 2: Database Queries
@@ -409,112 +419,110 @@
::
- #-----------------------------------------------------------------
- #
- # This example demonstrates the creation of a DSL for database
- # queries allowing 'and' and 'or' operators to be used to
- # formulate the query.
- #
- #-----------------------------------------------------------------
-
- class SQLNode:
-
- def __and2__(self, other):
- return SQLBinop("and", self, other)
-
- def __rand2__(self, other):
- return SQLBinop("and", other, self)
-
- def __eq__(self, other):
- return SQLBinop("=", self, other)
-
-
- class Table(SQLNode):
-
- def __init__(self, name):
- self.__tablename__ = name
-
- def __getattr__(self, name):
- return SQLAttr(self, name)
-
- def __sql__(self):
- return self.__tablename__
-
-
- class SQLBinop(SQLNode):
-
- def __init__(self, op, opnd1, opnd2):
- self.op = op.upper()
- self.opnd1 = opnd1
- self.opnd2 = opnd2
-
- def __sql__(self):
- return "(%s %s %s)" % (sql(self.opnd1), self.op, sql(self.opnd2))
-
-
- class SQLAttr(SQLNode):
-
- def __init__(self, table, name):
- self.table = table
- self.name = name
-
- def __sql__(self):
- return "%s.%s" % (sql(self.table), self.name)
-
-
- class SQLSelect(SQLNode):
-
- def __init__(self, targets):
- self.targets = targets
- self.where_clause = None
-
- def where(self, expr):
- self.where_clause = expr
- return self
-
- def __sql__(self):
- result = "SELECT %s" % ", ".join(sql(target) for target in self.targets)
- if self.where_clause:
- result = "%s WHERE %s" % (result, sql(self.where_clause))
- return result
-
-
- def sql(expr):
- if isinstance(expr, SQLNode):
- return expr.__sql__()
- elif isinstance(expr, str):
- return "'%s'" % expr.replace("'", "''")
- else:
- return str(expr)
-
-
- def select(*targets):
- return SQLSelect(targets)
-
-
-#--------------------------------------------------------------------------------
-
-::
- dishes = Table("dishes")
- customers = Table("customers")
- orders = Table("orders")
-
- query = select(customers.name, dishes.price, orders.amount).where(
- customers.cust_id == orders.cust_id and orders.dish_id == dishes.dish_id
- and dishes.name == "Spam, Eggs, Sausages and Spam")
-
- print(repr(query))
- print(sql(query))
+ #-----------------------------------------------------------------
+ #
+ # This example demonstrates the creation of a DSL for database
+ # queries allowing 'and' and 'or' operators to be used to
+ # formulate the query.
+ #
+ #-----------------------------------------------------------------
+
+ class SQLNode(object):
+
+ def __and2__(self, other):
+ return SQLBinop("and", self, other)
+
+ def __rand2__(self, other):
+ return SQLBinop("and", other, self)
+
+ def __eq__(self, other):
+ return SQLBinop("=", self, other)
+
+
+ class Table(SQLNode):
+
+ def __init__(self, name):
+ self.__tablename__ = name
+
+ def __getattr__(self, name):
+ return SQLAttr(self, name)
+
+ def __sql__(self):
+ return self.__tablename__
+
+
+ class SQLBinop(SQLNode):
+
+ def __init__(self, op, opnd1, opnd2):
+ self.op = op.upper()
+ self.opnd1 = opnd1
+ self.opnd2 = opnd2
+
+ def __sql__(self):
+ return "(%s %s %s)" % (sql(self.opnd1), self.op, sql(self.opnd2))
+
+
+ class SQLAttr(SQLNode):
+
+ def __init__(self, table, name):
+ self.table = table
+ self.name = name
+
+ def __sql__(self):
+ return "%s.%s" % (sql(self.table), self.name)
+
+
+ class SQLSelect(SQLNode):
+
+ def __init__(self, targets):
+ self.targets = targets
+ self.where_clause = None
+
+ def where(self, expr):
+ self.where_clause = expr
+ return self
+
+ def __sql__(self):
+ result = "SELECT %s" % ", ".join([sql(target) for target in self.targets])
+ if self.where_clause:
+ result = "%s WHERE %s" % (result, sql(self.where_clause))
+ return result
+
+
+ def sql(expr):
+ if isinstance(expr, SQLNode):
+ return expr.__sql__()
+ elif isinstance(expr, str):
+ return "'%s'" % expr.replace("'", "''")
+ else:
+ return str(expr)
+
+
+ def select(*targets):
+ return SQLSelect(targets)
+
+ #-----------------------------------------------------------------
+
+ dishes = Table("dishes")
+ customers = Table("customers")
+ orders = Table("orders")
+
+ query = select(customers.name, dishes.price, orders.amount).where(
+ customers.cust_id == orders.cust_id and orders.dish_id == dishes.dish_id
+ and dishes.name == "Spam, Eggs, Sausages and Spam")
+
+ print repr(query)
+ print sql(query)
Example 2 Output
----------------
::
- <__main__.SQLSelect object at 0x1cc830>
- SELECT customers.name, dishes.price, orders.amount WHERE
- (((customers.cust_id = orders.cust_id) AND (orders.dish_id =
- dishes.dish_id)) AND (dishes.name = 'Spam, Eggs, Sausages and Spam'))
+ <__main__.SQLSelect object at 0x1cc830>
+ SELECT customers.name, dishes.price, orders.amount WHERE
+ (((customers.cust_id = orders.cust_id) AND (orders.dish_id =
+ dishes.dish_id)) AND (dishes.name = 'Spam, Eggs, Sausages and Spam'))
Copyright
--
Repository URL: http://hg.python.org/peps
More information about the Python-checkins
mailing list