code: def where(self, wherefn=None, **kwargs):
          
          if kwargs:
              # order query criteria in ascending order of number of matching items
              # for each individual given attribute; this will minimize the number 
              # of filtering records that each subsequent attribute will have to
              # handle
              kwargs = list(kwargs.items())
              if len(kwargs) > 1 and len(self) > 100:
                  kwargs = sorted(kwargs, key=self._query_attr_sort_fn)
                  
              ret = self
              NO_SUCH_ATTR = object()
              for k, v in kwargs:
                  newret = ret.copy_template()
                  if k in ret._indexes:
                      newret.insert_many(ret._indexes[k][v])
                  else:
                      newret.insert_many(r for r in ret.obs if getattr(r, k, NO_SUCH_ATTR) == v)
                  ret = newret
                  if not ret:
                      break
          else:
              ret = self.clone()
  
          if ret and wherefn is not None:
              newret = ret.copy_template()
              newret.insert_many(filter(wherefn, ret.obs))
              ret = newret
  
          return ret
docs: Retrieves matching objects from the table, based on given
          named parameters.  If multiple named parameters are given, then
          only objects that satisfy all of the query criteria will be returned.
          
          @param wherefn: a method or lambda that returns a boolean result, as in::
             
             lambda ob : ob.unitprice > 10
             
          @type wherefn: callable(object) returning boolean
  
          @param kwargs: attributes for selecting records, given as additional 
            named arguments of the form C{attrname="attrvalue"}.
  
          @return: a new Table containing the matching objects