[Zope-CVS] CVS: Packages/pypes/pypes - interfaces.py:1.19 query.py:1.7

Casey Duncan casey at zope.com
Mon Apr 19 01:13:16 EDT 2004


Update of /cvs-repository/Packages/pypes/pypes
In directory cvs.zope.org:/tmp/cvs-serv10739

Modified Files:
	interfaces.py query.py 
Log Message:
Replace xproduct generator with CartProduct class which is lazy and will support later manipulation efficiently.


=== Packages/pypes/pypes/interfaces.py 1.18 => 1.19 ===
--- Packages/pypes/pypes/interfaces.py:1.18	Tue Apr 13 00:15:28 2004
+++ Packages/pypes/pypes/interfaces.py	Mon Apr 19 01:12:45 2004
@@ -633,5 +633,5 @@
     """Expression with order information"""
     
     order = Attribute('order', """1 == ascending, -1 == descending""")
-    
+
     


=== Packages/pypes/pypes/query.py 1.6 => 1.7 ===
--- Packages/pypes/pypes/query.py:1.6	Mon Apr 12 21:03:05 2004
+++ Packages/pypes/pypes/query.py	Mon Apr 19 01:12:45 2004
@@ -45,6 +45,7 @@
 
 $Id$"""
 
+from operator import mul
 from zope.interface import implements
 from BTrees.OOBTree import OOBTree
 from pypes.exceptions import PypesLookupError, PypesQueryInputError
@@ -54,66 +55,77 @@
 descending = 'descending'
 ascending = 'ascending'
 
-## Query Operation Primitives ##
+## Query Primitives ##
 
-def xproduct(*named_inputs, **inputs):
-    """A generator of dicts comprising the cross product of the inputs which
-    are iterable sequences of objects.
-    
-    The yielded dicts have each input name and one input member as their
-    respective keys and values. inputs are either self-named on which case
-    their name is derived from their __name__ attribute. self-named inputs
-    are provided as positional arguments (order is not significant). Inputs
-    which are not named, or for which an alias is desired are passed in as
-    keyword arguments where the keyword denotes the name.
-    
-    Inputs may be any iterable sequence of objects. In general inputs are 
-    homogenous sets of objects.
-    
-    Note that the yielded dict object is reused for each generation. Because
-    of this, the application *must* make a copy of this dict if it wishes
-    to persist a reference to it after the next one is generated.
-    This is to efficiently handle the common case, where the generated
-    product will be filtered and only a fraction of the generated "rows"
-    will be used. Since scan_inputs generates a quantity of results equal
-    to all of the inputs multiplied together, this greatly reduces the
-    number of objects created by the scan
+class CartProduct:
+    """Cartesian product of one or more inputs to a query. Allows lazy 
+    evaluation, and efficient combinatorial operations
     """
-    for ipt in named_inputs:
-        try:
-            name = ipt.__name__
-        except AttributeError:
-            raise PypesQueryInputError, 'unamed input %r' % ipt
-        if name not in inputs:
-            inputs[name] = ipt
+
+    def __init__(self, input_map, criteria=None):
+        """Construct the product
+        
+        input_map -- a mapping object mapping names => inputs.
+        
+        criteria -- optional criteria expression, an IExpression object. 
+        Used to select the input items to create a partial filtered product.
+        If omitted then a full product is generated.
+        """
+        self.inputs = input_map
+        self.criteria = criteria
+        
+    def maxLen(self):
+        """Return the maximum integer length of the product. This value 
+        is calculated by multiplying the lengths of the inputs together.
+        This value matches the actual length for a full product.
+        
+        This method assumes all the inputs support __len__
+        """
+        return reduce(mul, [len(ipt) for ipt in self.inputs.values()])
+    
+    def iterProduct(self, *names):
+        """Iterate the cartesian product yielding items from the inputs named.
+        If a single name is specified, yield each matching item from the input.
+        If multiple names are specified, yield tuples of the matching items
+        from the cooresponding named inputs.
+        """
+        criteria = self.criteria
+        # Prime the input iterators and get the first row
+        row = {}
+        input_iters = []
+        for name, ipt in self.inputs.items():
+            obj_iter = iter(ipt) # XXX won't work with input iters
+            input_iters.append((name, obj_iter))
+            row[name] = obj_iter.next()
+        # Create output factory from input names
+        if len(names) == 1:
+            out, = names
+            output = lambda row: row[out]
+        elif len(names) == 2:
+            out1, out2 = names
+            output = lambda row: (row[out1], row[out2])
         else:
-            raise PypesQueryInputError, 'duplicate input name %s' % name
-    # Prime the input iterators and get the first row
-    row = {}
-    input_iters = []
-    for name, ipt in inputs.items():
-        obj_iter = iter(ipt)
-        input_iters.append((name, obj_iter))
-        row[name] = obj_iter.next()
-    yield row
-    # Get subsequent rows by combining inputs
-    i = 0
-    while True:
-        try:
-            name, obj_iter = input_iters[i]
-        except IndexError:
-            break
+            output = lambda row: tuple([row[n] for n in names])
+        if criteria is None or criteria(row):
+            yield output(row)
+        # Get subsequent rows by combining inputs
+        i = 0
         while True:
             try:
-                row[name] = obj_iter.next()
-            except StopIteration:
-                obj_iter = iter(inputs[name])
-                input_iters[i] = name, obj_iter
-                row[name] = obj_iter.next()
-                i += 1
+                name, obj_iter = input_iters[i]
+            except IndexError:
                 break
-            else:
-                yield row
+            while True:
+                try:
+                    row[name] = obj_iter.next()
+                except StopIteration:
+                    obj_iter = iter(self.inputs[name])
+                    input_iters[i] = name, obj_iter
+                    row[name] = obj_iter.next()
+                    i += 1
+                    break   
+                if criteria is None or criteria(row):
+                    yield output(row)
                 if i > 0:
                     i = 0
                     break




More information about the Zope-CVS mailing list