Error when returning embedded transformers in Jupyter notebook

### Describe the bug

When creating a custom transformer object that includes a transformer type as an instance, a `TypeError` is thrown if the object is returned at the end of a Jupyter cell. This does not cause an error in the terminal, but raises an error during the conversion to an HTML object for Jupyter notebooks. Weirdly, the object is created and returned, but an error is thrown when Jupyter attempts to display it.

### Steps/Code to Reproduce

```python
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.feature_selection import VarianceThreshold

class EmbeddedTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, transformer):
        self.transformer = transformer
        
    def fit(self, X=None, y=None):
        return self
            
    def transform(self, X=None):
        return X

EmbeddedTransformer(VarianceThreshold())  # No error
EmbeddedTransformer('hello')  # No error
t = EmbeddedTransformer(VarianceThreshold)  # No error
EmbeddedTransformer(VarianceThreshold)  # ERROR
```

### Expected Results

No error is thrown, and the HTML representation of the transformer is shown in the Jupyter cell.

### Actual Results

Here's the full error traceback:
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/IPython/core/formatters.py:973, in MimeBundleFormatter.__call__(self, obj, include, exclude)
    970     method = get_real_method(obj, self.print_method)
    972     if method is not None:
--> 973         return method(include=include, exclude=exclude)
    974     return None
    975 else:

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/base.py:631, in BaseEstimator._repr_mimebundle_(self, **kwargs)
    629 output = {"text/plain": repr(self)}
    630 if get_config()["display"] == "diagram":
--> 631     output["text/html"] = estimator_html_repr(self)
    632 return output

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:410, in estimator_html_repr(estimator)
    396 fallback_msg = (
    397     "In a Jupyter environment, please rerun this cell to show the HTML"
    398     " representation or trust the notebook. <br />On GitHub, the"
    399     " HTML representation is unable to render, please try loading this page"
    400     " with nbviewer.org."
    401 )
    402 out.write(
    403     f"<style>{style_with_id}</style>"
    404     f'<div id="{container_id}" class="sk-top-container">'
   (...)
    408     '<div class="sk-container" hidden>'
    409 )
--> 410 _write_estimator_html(
    411     out,
    412     estimator,
    413     estimator.__class__.__name__,
    414     estimator_str,
    415     first_call=True,
    416 )
    417 out.write("</div></div>")
    419 html_output = out.getvalue()

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:168, in _write_estimator_html(out, estimator, estimator_label, estimator_label_details, first_call)
    166         # wrap element in a serial visualblock
    167         serial_block = _VisualBlock("serial", [est], dash_wrapped=False)
--> 168         _write_estimator_html(out, serial_block, name, name_details)
    169         out.write("</div>")  # sk-parallel-item
    171 out.write("</div></div>")

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:163, in _write_estimator_html(out, estimator, estimator_label, estimator_label_details, first_call)
    161 for est, name, name_details in est_infos:
    162     if kind == "serial":
--> 163         _write_estimator_html(out, est, name, name_details)
    164     else:  # parallel
    165         out.write('<div class="sk-parallel-item">')

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:147, in _write_estimator_html(out, estimator, estimator_label, estimator_label_details, first_call)
    145 else:
    146     with config_context(print_changed_only=True):
--> 147         est_block = _get_visual_block(estimator)
    149 if est_block.kind in ("serial", "parallel"):
    150     dashed_wrapped = first_call or est_block.dash_wrapped

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:120, in _get_visual_block(estimator)
    116 # check if estimator looks like a meta estimator wraps estimators
    117 if hasattr(estimator, "get_params"):
    118     estimators = [
    119         (key, est)
--> 120         for key, est in estimator.get_params(deep=False).items()
    121         if hasattr(est, "get_params") and hasattr(est, "fit")
    122     ]
    123     if estimators:
    124         return _VisualBlock(
    125             "parallel",
    126             [est for _, est in estimators],
    127             names=[f"{key}: {est.__class__.__name__}" for key, est in estimators],
    128             name_details=[str(est) for _, est in estimators],
    129         )

TypeError: get_params() missing 1 required positional argument: 'self'

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/IPython/core/formatters.py:343, in BaseFormatter.__call__(self, obj)
    341     method = get_real_method(obj, self.print_method)
    342     if method is not None:
--> 343         return method()
    344     return None
    345 else:

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/base.py:625, in BaseEstimator._repr_html_inner(self)
    620 def _repr_html_inner(self):
    621     """This function is returned by the @property `_repr_html_` to make
    622     `hasattr(estimator, "_repr_html_") return `True` or `False` depending
    623     on `get_config()["display"]`.
    624     """
--> 625     return estimator_html_repr(self)

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:410, in estimator_html_repr(estimator)
    396 fallback_msg = (
    397     "In a Jupyter environment, please rerun this cell to show the HTML"
    398     " representation or trust the notebook. <br />On GitHub, the"
    399     " HTML representation is unable to render, please try loading this page"
    400     " with nbviewer.org."
    401 )
    402 out.write(
    403     f"<style>{style_with_id}</style>"
    404     f'<div id="{container_id}" class="sk-top-container">'
   (...)
    408     '<div class="sk-container" hidden>'
    409 )
--> 410 _write_estimator_html(
    411     out,
    412     estimator,
    413     estimator.__class__.__name__,
    414     estimator_str,
    415     first_call=True,
    416 )
    417 out.write("</div></div>")
    419 html_output = out.getvalue()

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:168, in _write_estimator_html(out, estimator, estimator_label, estimator_label_details, first_call)
    166         # wrap element in a serial visualblock
    167         serial_block = _VisualBlock("serial", [est], dash_wrapped=False)
--> 168         _write_estimator_html(out, serial_block, name, name_details)
    169         out.write("</div>")  # sk-parallel-item
    171 out.write("</div></div>")

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:163, in _write_estimator_html(out, estimator, estimator_label, estimator_label_details, first_call)
    161 for est, name, name_details in est_infos:
    162     if kind == "serial":
--> 163         _write_estimator_html(out, est, name, name_details)
    164     else:  # parallel
    165         out.write('<div class="sk-parallel-item">')

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:147, in _write_estimator_html(out, estimator, estimator_label, estimator_label_details, first_call)
    145 else:
    146     with config_context(print_changed_only=True):
--> 147         est_block = _get_visual_block(estimator)
    149 if est_block.kind in ("serial", "parallel"):
    150     dashed_wrapped = first_call or est_block.dash_wrapped

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/_estimator_html_repr.py:120, in _get_visual_block(estimator)
    116 # check if estimator looks like a meta estimator wraps estimators
    117 if hasattr(estimator, "get_params"):
    118     estimators = [
    119         (key, est)
--> 120         for key, est in estimator.get_params(deep=False).items()
    121         if hasattr(est, "get_params") and hasattr(est, "fit")
    122     ]
    123     if estimators:
    124         return _VisualBlock(
    125             "parallel",
    126             [est for _, est in estimators],
    127             names=[f"{key}: {est.__class__.__name__}" for key, est in estimators],
    128             name_details=[str(est) for _, est in estimators],
    129         )

TypeError: get_params() missing 1 required positional argument: 'self'
```

After that error message, we get a representation of the object:
`EmbeddedTransformer(transformer=<class 'sklearn.feature_selection._variance_threshold.VarianceThreshold'>)`

### Versions

```shell
System:
    python: 3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 06:04:18)  [GCC 10.3.0]
executable: /home/ec2-user/anaconda3/envs/prodenv/bin/python
   machine: Linux-4.14.287-148.504.amzn1.x86_64-x86_64-with-glibc2.10

Python dependencies:
      sklearn: 1.1.0
          pip: 22.1.2
   setuptools: 63.2.0
        numpy: 1.22.0
        scipy: 1.8.0
       Cython: 0.29.30
       pandas: 1.4.2
   matplotlib: 3.5.2
       joblib: 1.0.1
threadpoolctl: 3.1.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
         prefix: libopenblas
       filepath: /home/ec2-user/SageMaker/kernels/prodenv/lib/libopenblasp-r0.3.21.so
        version: 0.3.21
threading_layer: pthreads
   architecture: SkylakeX
    num_threads: 8

       user_api: openmp
   internal_api: openmp
         prefix: libgomp
       filepath: /home/ec2-user/SageMaker/kernels/prodenv/lib/python3.8/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None
    num_threads: 8

       user_api: blas
   internal_api: openblas
         prefix: libopenblas
       filepath: /home/ec2-user/SageMaker/kernels/prodenv/lib/python3.8/site-packages/scipy.libs/libopenblasp-r0-8b9e111f.3.17.so
        version: 0.3.17
threading_layer: pthreads
   architecture: SkylakeX
    num_threads: 8
```

COMMENT:
A similar error appears when using an embedded transformer in a `Pipeline`.  This one is a bit more serious, since it actually means we can't fit the models -- it's not just related to the Jupyter display.

Would it be better to submit this as a new issue, or leave it here?

Here's a minimal working example:

```python
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer


class EmbeddedTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, transformer):
        self.transformer = transformer
        
    def fit(self, X=None, y=None):
        return self
            
    def transform(self, X=None):
        return X
    
    
X, y = make_classification(n_samples=100, n_features=20)
    
est = Pipeline([
    ('embedded_transformer', EmbeddedTransformer(FunctionTransformer)),
    ('mdl', RandomForestClassifier())
])

search_spaces = {
    'mdl__max_depth': np.arange(2, 5),
}

# Raises an error
search = GridSearchCV(est, search_spaces,
                      scoring='roc_auc',
                      n_jobs=1)
search.fit(X, y)
```

And here's the full traceback:
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [16], in <cell line: 36>()
     28 search_spaces = {
     29     'mdl__max_depth': np.arange(2, 5),
     30 }
     32 search = GridSearchCV(est, search_spaces,
     33                       scoring='roc_auc',
     34                       n_jobs=1)
---> 36 search.fit(X, y)

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/model_selection/_search.py:875, in BaseSearchCV.fit(self, X, y, groups, **fit_params)
    869     results = self._format_results(
    870         all_candidate_params, n_splits, all_out, all_more_results
    871     )
    873     return results
--> 875 self._run_search(evaluate_candidates)
    877 # multimetric is determined here because in the case of a callable
    878 # self.scoring the return type is only known after calling
    879 first_test_score = all_out[0]["test_scores"]

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/model_selection/_search.py:1375, in GridSearchCV._run_search(self, evaluate_candidates)
   1373 def _run_search(self, evaluate_candidates):
   1374     """Search all candidates in param_grid"""
-> 1375     evaluate_candidates(ParameterGrid(self.param_grid))

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/model_selection/_search.py:822, in BaseSearchCV.fit.<locals>.evaluate_candidates(candidate_params, cv, more_results)
    814 if self.verbose > 0:
    815     print(
    816         "Fitting {0} folds for each of {1} candidates,"
    817         " totalling {2} fits".format(
    818             n_splits, n_candidates, n_candidates * n_splits
    819         )
    820     )
--> 822 out = parallel(
    823     delayed(_fit_and_score)(
    824         clone(base_estimator),
    825         X,
    826         y,
    827         train=train,
    828         test=test,
    829         parameters=parameters,
    830         split_progress=(split_idx, n_splits),
    831         candidate_progress=(cand_idx, n_candidates),
    832         **fit_and_score_kwargs,
    833     )
    834     for (cand_idx, parameters), (split_idx, (train, test)) in product(
    835         enumerate(candidate_params), enumerate(cv.split(X, y, groups))
    836     )
    837 )
    839 if len(out) < 1:
    840     raise ValueError(
    841         "No fits were performed. "
    842         "Was the CV iterator empty? "
    843         "Were there no candidates?"
    844     )

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/joblib/parallel.py:1041, in Parallel.__call__(self, iterable)
   1032 try:
   1033     # Only set self._iterating to True if at least a batch
   1034     # was dispatched. In particular this covers the edge
   (...)
   1038     # was very quick and its callback already dispatched all the
   1039     # remaining jobs.
   1040     self._iterating = False
-> 1041     if self.dispatch_one_batch(iterator):
   1042         self._iterating = self._original_iterator is not None
   1044     while self.dispatch_one_batch(iterator):

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/joblib/parallel.py:859, in Parallel.dispatch_one_batch(self, iterator)
    857     return False
    858 else:
--> 859     self._dispatch(tasks)
    860     return True

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/joblib/parallel.py:777, in Parallel._dispatch(self, batch)
    775 with self._lock:
    776     job_idx = len(self._jobs)
--> 777     job = self._backend.apply_async(batch, callback=cb)
    778     # A job can complete so quickly than its callback is
    779     # called before we get here, causing self._jobs to
    780     # grow. To ensure correct results ordering, .insert is
    781     # used (rather than .append) in the following line
    782     self._jobs.insert(job_idx, job)

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/joblib/_parallel_backends.py:208, in SequentialBackend.apply_async(self, func, callback)
    206 def apply_async(self, func, callback=None):
    207     """Schedule a func to be run"""
--> 208     result = ImmediateResult(func)
    209     if callback:
    210         callback(result)

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/joblib/_parallel_backends.py:572, in ImmediateResult.__init__(self, batch)
    569 def __init__(self, batch):
    570     # Don't delay the application, to avoid keeping the input
    571     # arguments in memory
--> 572     self.results = batch()

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/joblib/parallel.py:262, in BatchedCalls.__call__(self)
    258 def __call__(self):
    259     # Set the default nested backend to self._backend but do not set the
    260     # change the default number of processes to -1
    261     with parallel_backend(self._backend, n_jobs=self._n_jobs):
--> 262         return [func(*args, **kwargs)
    263                 for func, args, kwargs in self.items]

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/joblib/parallel.py:262, in <listcomp>(.0)
    258 def __call__(self):
    259     # Set the default nested backend to self._backend but do not set the
    260     # change the default number of processes to -1
    261     with parallel_backend(self._backend, n_jobs=self._n_jobs):
--> 262         return [func(*args, **kwargs)
    263                 for func, args, kwargs in self.items]

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/fixes.py:117, in _FuncWrapper.__call__(self, *args, **kwargs)
    115 def __call__(self, *args, **kwargs):
    116     with config_context(**self.config):
--> 117         return self.function(*args, **kwargs)

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/model_selection/_validation.py:674, in _fit_and_score(estimator, X, y, scorer, train, test, verbose, parameters, fit_params, return_train_score, return_parameters, return_n_test_samples, return_times, return_estimator, split_progress, candidate_progress, error_score)
    671     for k, v in parameters.items():
    672         cloned_parameters[k] = clone(v, safe=False)
--> 674     estimator = estimator.set_params(**cloned_parameters)
    676 start_time = time.time()
    678 X_train, y_train = _safe_split(estimator, X, y, train)

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/pipeline.py:188, in Pipeline.set_params(self, **kwargs)
    169 def set_params(self, **kwargs):
    170     """Set the parameters of this estimator.
    171 
    172     Valid parameter keys can be listed with ``get_params()``. Note that
   (...)
    186         Pipeline class instance.
    187     """
--> 188     self._set_params("steps", **kwargs)
    189     return self

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/metaestimators.py:72, in _BaseComposition._set_params(self, attr, **params)
     69                 self._replace_estimator(attr, name, params.pop(name))
     71 # 3. Step parameters and other initialisation arguments
---> 72 super().set_params(**params)
     73 return self

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/base.py:239, in BaseEstimator.set_params(self, **params)
    236 if not params:
    237     # Simple optimization to gain speed (inspect is slow)
    238     return self
--> 239 valid_params = self.get_params(deep=True)
    241 nested_params = defaultdict(dict)  # grouped by prefix
    242 for key, value in params.items():

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/pipeline.py:167, in Pipeline.get_params(self, deep)
    150 def get_params(self, deep=True):
    151     """Get parameters for this estimator.
    152 
    153     Returns the parameters given in the constructor as well as the
   (...)
    165         Parameter names mapped to their values.
    166     """
--> 167     return self._get_params("steps", deep=deep)

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/utils/metaestimators.py:50, in _BaseComposition._get_params(self, attr, deep)
     48 for name, estimator in estimators:
     49     if hasattr(estimator, "get_params"):
---> 50         for key, value in estimator.get_params(deep=True).items():
     51             out["%s__%s" % (name, key)] = value
     52 return out

File ~/anaconda3/envs/prodenv/lib/python3.8/site-packages/sklearn/base.py:213, in BaseEstimator.get_params(self, deep)
    211 value = getattr(self, key)
    212 if deep and hasattr(value, "get_params"):
--> 213     deep_items = value.get_params().items()
    214     out.update((key + "__" + k, val) for k, val in deep_items)
    215 out[key] = value

TypeError: get_params() missing 1 required positional argument: 'self'
```

Labels: Bug