[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