Don't print the estimator like a dict (`{...}`) beyond `maxlevels`

### Describe the bug

Estimator pretty printer doesn't render deeply-nested estimators correctly.

### Steps/Code to Reproduce

```pycon
>>> from sklearn.base import BaseEstimator
>>> from sklearn.utils._pprint import _EstimatorPrettyPrinter
>>>
>>>
>>> # Constructors excerpted to test pprinting
>>> class LogisticRegression(BaseEstimator):
...     def __init__(
...         self,
...         penalty="l2",
...         dual=False,
...         tol=1e-4,
...         C=1.0,
...         fit_intercept=True,
...         intercept_scaling=1,
...         class_weight=None,
...         random_state=None,
...         solver="warn",
...         max_iter=100,
...         multi_class="warn",
...         verbose=0,
...         warm_start=False,
...         n_jobs=None,
...         l1_ratio=None,
...     ):
...         self.penalty = penalty
...         self.dual = dual
...         self.tol = tol
...         self.C = C
...         self.fit_intercept = fit_intercept
...         self.intercept_scaling = intercept_scaling
...         self.class_weight = class_weight
...         self.random_state = random_state
...         self.solver = solver
...         self.max_iter = max_iter
...         self.multi_class = multi_class
...         self.verbose = verbose
...         self.warm_start = warm_start
...         self.n_jobs = n_jobs
...         self.l1_ratio = l1_ratio
...     def fit(self, X, y):
...         return self
... 
>>>
>>> class RFE(BaseEstimator):
...     def __init__(self, estimator, n_features_to_select=None, step=1, verbose=0):
...         self.estimator = estimator
...         self.n_features_to_select = n_features_to_select
...         self.step = step
...         self.verbose = verbose
... 
>>>
>>> pp = _EstimatorPrettyPrinter(depth=1)
>>> rfe = RFE(RFE(RFE(RFE(RFE(LogisticRegression())))))
>>> pp.pformat(rfe)
'RFE(estimator={...})'
```

### Expected Results

```pycon
>>> pp.pformat(rfe)
'RFE(estimator=RFE(...), n_features_to_select=None, step=1, verbose=0)'
```

### Actual Results

```pycon
>>> pp.pformat(rfe)
'RFE(estimator={...})'
```

### Versions

```shell
System:
    python: 3.11.8 (main, Feb 26 2024, 15:36:12) [Clang 14.0.6 ]
executable: /opt/miniconda3/envs/ibis-ml-all/bin/python
   machine: macOS-14.5-arm64-arm-64bit

Python dependencies:
      sklearn: 1.5.0
          pip: 24.0
   setuptools: 69.5.1
        numpy: 1.26.4
        scipy: 1.13.1
       Cython: None
       pandas: 2.2.2
   matplotlib: None
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libopenblas
       filepath: /opt/miniconda3/envs/ibis-ml-all/lib/python3.11/site-packages/numpy/.dylibs/libopenblas64_.0.dylib
        version: 0.3.23.dev
threading_layer: pthreads
   architecture: armv8

       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libopenblas
       filepath: /opt/miniconda3/envs/ibis-ml-all/lib/python3.11/site-packages/scipy/.dylibs/libopenblas.0.dylib
        version: 0.3.27
threading_layer: pthreads
   architecture: neoversen1

       user_api: openmp
   internal_api: openmp
    num_threads: 8
         prefix: libomp
       filepath: /opt/miniconda3/envs/ibis-ml-all/lib/python3.11/site-packages/sklearn/.dylibs/libomp.dylib
        version: None
```

COMMENT:
The `test_depth` unit test does set `changed_only` to `False`. The issue is
not related to that, but the way that the estimator representation is
condensed. Instead of `{...}` (which makes sense for how you show a dict
beyond a certain depth), it should look like `MyEstimator(...)`. There is
similar code in the pretty printer already for how you should show a list
beyond certain depth as `[...]`, etc.

On Thu, Jul 18, 2024, 1:57 AM Md Tahseen Anam ***@***.***>
wrote:

> Hi, what I meant is you can set pp._changed_only = False and this should
> solve your issue. The logic is already implemented in the code I think. So
> I was wondering why the changes made in 29492
> <https://github.com/scikit-learn/scikit-learn/pull/29492> is necessary.
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/scikit-learn/scikit-learn/issues/29421#issuecomment-2235867059>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ADK3W3QTZYSNDTQINMXGNGLZM5YPHAVCNFSM6AAAAABKNNP7NCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMZVHA3DOMBVHE>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>

Labels: Bug