PR proposal to solve "Bunch object returns a regular dict when calling `copy` method on it"

### Describe the bug

If I do

```python
bunch = Bunch (message='hello')
should_be_bunch = bunch.copy()
print (should_be_bunch.message)
```

I get a (for me) unexpected error, because `should_be_bunch` is actually a `dict`. This is easily fixable and I can submit a PR if this is of interest.

### Steps/Code to Reproduce

```python
from sklearn.utils import Bunch

bunch = Bunch (message='hello')
should_be_bunch = bunch.copy()
print (should_be_bunch.message)
```

### Expected Results

```pytb
Ouput in console: 
hello

No errors expected

### Actual Results

AttributeError                            Traceback (most recent call last)
Cell In[2], line 3
      1 bunch = Bunch (message='hello')
      2 should_be_bunch = bunch.copy()
----> 3 print (should_be_bunch.message)

AttributeError: 'dict' object has no attribute 'message'
```

### Versions

```shell
System:
    python: 3.10.9 (main, Mar  1 2023, 18:23:06) [GCC 11.2.0]
executable: /home/jaumeamllo/miniconda3/envs/tsforecast/bin/python
   machine: Linux-5.15.133.1-microsoft-standard-WSL2-x86_64-with-glibc2.35

Python dependencies:
      sklearn: 1.2.1
          pip: 22.3.1
   setuptools: 65.6.3
        numpy: 1.23.5
        scipy: 1.10.0
       Cython: 0.29.33
       pandas: 1.5.3
   matplotlib: 3.7.1
       joblib: 1.2.0
threadpoolctl: 2.2.0

Built with OpenMP: True
Exception ignored on calling ctypes callback function: <function _ThreadpoolInfo._find_modules_with_dl_iterate_phdr.<locals>.match_module_callback at 0x7f5e7a96cca0>
Traceback (most recent call last):
  File "/home/jaumeamllo/miniconda3/envs/tsforecast/lib/python3.10/site-packages/threadpoolctl.py", line 400, in match_module_callback
    self._make_module_from_path(filepath)
  File "/home/jaumeamllo/miniconda3/envs/tsforecast/lib/python3.10/site-packages/threadpoolctl.py", line 515, in _make_module_from_path
    module = module_class(filepath, prefix, user_api, internal_api)
  File "/home/jaumeamllo/miniconda3/envs/tsforecast/lib/python3.10/site-packages/threadpoolctl.py", line 606, in __init__
    self.version = self.get_version()
  File "/home/jaumeamllo/miniconda3/envs/tsforecast/lib/python3.10/site-packages/threadpoolctl.py", line 646, in get_version
    config = get_config().split()
AttributeError: 'NoneType' object has no attribute 'split'

threadpoolctl info:
       filepath: /home/jaumeamllo/miniconda3/envs/tsforecast/lib/libmkl_rt.so.1
         prefix: libmkl_rt
       user_api: blas
   internal_api: mkl
        version: 2021.4-Product
    num_threads: 2
threading_layer: intel

       filepath: /home/jaumeamllo/miniconda3/envs/tsforecast/lib/libgomp.so.1.0.0
         prefix: libgomp
       user_api: openmp
   internal_api: openmp
        version: None
    num_threads: 4

       filepath: /home/jaumeamllo/miniconda3/envs/tsforecast/lib/libiomp5.so
         prefix: libiomp
       user_api: openmp
   internal_api: openmp
        version: None
    num_threads: 4
```

COMMENT:
Note that the behavior is as expected when creating a copy using ``copy.copy()`` or ``copy.deepcopy()``. @JaumeAmoresDS maybe it's enough for your use case.

If we really want `Bunch.copy()` to have the expected behavior, we need to override the `copy` method:
```py
    def copy(self):
        new_bunch = Bunch()
        new_bunch.update(self)
        new_bunch.__dict__.update(self.__dict__)
        return new_bunch
```
+0.5 on my side. @glemaitre do you think it's worth ?

Labels: Enhancement