diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 98c5a72..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,29 +0,0 @@
-BSD 3-Clause License
-
-Copyright (c) 2017, Joel Nothman
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
-  list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
-  this list of conditions and the following disclaimer in the documentation
-  and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its
-  contributors may be used to endorse or promote products derived from
-  this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.rst b/README.rst
index dabcc64..e57dd0d 100644
--- a/README.rst
+++ b/README.rst
@@ -47,6 +47,11 @@ and use in Python::
     search = make_grid_search(estimator, cv=..., scoring=...)
     search.fit(X, y)
 
+Or search for the best among multiple distinct estimators/pipelines::
+
+    search = make_grid_search([estimator1, estimator2], cv=..., scoring=...)
+    search.fit(X, y)
+
 Motivating examples
 ...................
 
@@ -147,7 +152,6 @@ Searching over multiple grids.
         ...                 reduce=[kbest, pca])
         >>> gs = make_grid_search(pipe)
 
-
 .. |py-versions| image:: https://img.shields.io/pypi/pyversions/Django.svg
     :alt: Python versions supported
 
diff --git a/searchgrid.py b/searchgrid.py
index dcbff65..bbe4862 100644
--- a/searchgrid.py
+++ b/searchgrid.py
@@ -1,7 +1,8 @@
-from collections import Mapping
-import itertools
+from collections import Mapping as _Mapping
+import itertools as _itertools
 
 from sklearn.model_selection import GridSearchCV as _GridSearchCV
+from sklearn.pipeline import Pipeline as _Pipeline
 
 
 def set_grid(estimator, **grid):
@@ -32,7 +33,7 @@ def _update_grid(dest, src, prefix=None):
         src = [{prefix + k: v for k, v in d.items()}
                for d in src]
     out = []
-    for d1, d2 in itertools.product(dest, src):
+    for d1, d2 in _itertools.product(dest, src):
         out_d = d1.copy()
         out_d.update(d2)
         out.append(out_d)
@@ -41,7 +42,7 @@ def _update_grid(dest, src, prefix=None):
 
 def _build_param_grid(estimator):
     grid = getattr(estimator, '_param_grid', {})
-    if isinstance(grid, Mapping):
+    if isinstance(grid, _Mapping):
         grid = [grid]
 
     # handle estimator parameters having their own grids
@@ -100,14 +101,26 @@ def build_param_grid(estimator):
     return out
 
 
+def _check_estimator(estimator):
+    if isinstance(estimator, list):
+        estimator = set_grid(_Pipeline([('root', estimator[0])]),
+                             root=estimator)
+    elif not hasattr(estimator, 'fit'):
+        raise ValueError('Expected estimator, but %r does not have .fit'
+                         % estimator)
+    return estimator
+
+
 def make_grid_search(estimator, **kwargs):
     """Construct a GridSearchCV with the given estimator and its set grid
 
     Parameters
     ----------
-    estimator : scikit-learn compatible estimator
+    estimator : (list of) estimator
+        When a list, the estimators are searched over.
     kwargs
         Other parameters to the
         :class:`sklearn.model_selection.GridSearchCV` constructor.
     """
+    estimator = _check_estimator(estimator)
     return _GridSearchCV(estimator, build_param_grid(estimator), **kwargs)

make_grid_search(list)
One should not need to create a pipeline just to search over multiple alternative estimators.
diff --git a/test_searchgrid.py b/test_searchgrid.py
index fccfb1d..242b268 100644
--- a/test_searchgrid.py
+++ b/test_searchgrid.py
@@ -3,7 +3,8 @@ from sklearn.pipeline import Pipeline, make_pipeline
 from sklearn.svm import SVC
 from sklearn.linear_model import LogisticRegression, SGDClassifier
 from sklearn.feature_selection import SelectKBest
-from searchgrid import set_grid, build_param_grid
+from sklearn.datasets import load_iris
+from searchgrid import set_grid, build_param_grid, make_grid_search
 
 
 @pytest.mark.parametrize(('estimator', 'param_grid'), [
@@ -45,3 +46,21 @@ def test_build_param_grid_set_estimator():
                    'clf__degree': [2, 3], 'sel__k': [2, 3]},
                   {'clf': [clf2, clf4], 'sel__k': [2, 3]}]
     assert build_param_grid(estimator) == param_grid
+
+
+def test_make_grid_search():
+    X, y = load_iris(return_X_y=True)
+    lr = LogisticRegression()
+    svc = set_grid(SVC(), kernel=['poly'], degree=[2, 3])
+    gs1 = make_grid_search(lr, cv=5)  # empty grid
+    gs2 = make_grid_search(svc, cv=5)
+    gs3 = make_grid_search([lr, svc], cv=5)
+    for gs, n_results in [(gs1, 1), (gs2, 2), (gs3, 3)]:
+        gs.fit(X, y)
+        assert gs.cv == 5
+        assert len(gs.cv_results_['params']) == n_results
+
+    svc_mask = gs3.cv_results_['param_root'] == svc
+    assert svc_mask.sum() == 2
+    assert gs3.cv_results_['param_root__degree'][svc_mask].tolist() == [2, 3]
+    assert gs3.cv_results_['param_root'][~svc_mask].tolist() == [lr]

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov
3.6
doc/requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs==22.2.0
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
coverage==6.2
docutils==0.18.1
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
iniconfig==1.1.1
Jinja2==3.0.3
joblib==1.1.1
MarkupSafe==2.0.1
numpy==1.19.5
numpydoc==1.1.0
packaging==21.3
pluggy==1.0.0
py==1.11.0
Pygments==2.14.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
pytz==2025.2
requests==2.27.1
scikit-learn==0.24.2
scipy==1.5.4
-e git+https://github.com/jnothman/searchgrid.git@071a5c348caaf7f24889a7d0fd609c75521be3c9#egg=searchgrid
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
threadpoolctl==3.1.0
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: searchgrid
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - attrs==22.2.0
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - coverage==6.2
      - docutils==0.18.1
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - jinja2==3.0.3
      - joblib==1.1.1
      - markupsafe==2.0.1
      - numpy==1.19.5
      - numpydoc==1.1.0
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytz==2025.2
      - requests==2.27.1
      - scikit-learn==0.24.2
      - scipy==1.5.4
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - threadpoolctl==3.1.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - zipp==3.6.0
prefix: /opt/conda/envs/searchgrid

test_searchgrid.py::test_make_grid_search
test_searchgrid.py::test_build_param_grid[estimator0-param_grid0] test_searchgrid.py::test_build_param_grid[estimator1-param_grid1] test_searchgrid.py::test_build_param_grid[estimator2-param_grid2] test_searchgrid.py::test_build_param_grid_set_estimator
LICENSE searchgrid.py README.rst
LICENSE searchgrid.py README.rst