Make `KNeighborsClassifier.predict` and `KNeighborsRegressor.predict` react the same way to `X=None`

### Describe the workflow you want to enable

Currently `KNeighborsRegressor.predict()` accepts `None` as input, in which case it returns prediction for all samples in the training set based on the nearest neighbors not including the sample itself (consistent with `NearestNeighbors` behavior). However, `KNeighborsClassifier.predict()` does not accept `None` as input. This is inconsistent and should arguably be harmonized:

```Python
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor, NearestNeighbors
import numpy as np

X = np.random.normal(size=(10, 5))
y = np.random.normal(size=(10, 1))

knn = NearestNeighbors(n_neighbors=3)
knn.fit(X)
knn.kneighbors() # works

knn = KNeighborsRegressor(n_neighbors=3)
knn.fit(X, y)
knn.predict(None) # works (NB: does not work without "None")

knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X, np.ravel(y) > 0)
knn.predict(None) # fails with an error
```

### Describe your proposed solution

My proposed solution is to make `KNeighborsClassifier.predict(None)` behave the same as `KNeighborsRegressor.predict(None)`. As explained in https://github.com/scikit-learn/scikit-learn/issues/27747, the necessary fix requires changing only two lines of code.


### Additional context

As explained in https://github.com/scikit-learn/scikit-learn/issues/27747, this would be a great feature, super useful and convenient for computing LOOCV accuracy simply via `score(None, y)`. Using `score(X, y)` where `X` is the training set used in `fit(X)` gives a biased result because each (training set) sample gets included into its own neighbors.

COMMENT:
I did the following simulation, does it look reasonable to you @ogrisel?

![kfold](https://github.com/user-attachments/assets/24ee74b0-a623-42f5-841b-cf50e0748dd8)

You wrote that the notebook should not be part of scikit-learn repo. Where should I host it then, what's the good practice? The runtime is around 20 minutes for this particular setup.

```python
import pylab as plt
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import make_blobs
from sklearn.model_selection import cross_val_score, KFold

n_samples = 100
folds = [5, 10, 20, 25, 50, 100]
neighbors = [1, 5, 10, 20]
n_reps = 1000

cv_accs = np.zeros((n_reps, len(neighbors), len(folds)))

for seed in range(n_reps):
    print('.', end='', flush=True)
    
    X, y = make_blobs(n_samples=n_samples, centers=5, n_features=2, random_state=seed)
    
    for i, n_neighbors in enumerate(neighbors):
        nn_model = KNeighborsClassifier(n_neighbors=n_neighbors)
        
        for j, n_splits in enumerate(folds):
            cv_acc = np.mean(cross_val_score(nn_model, X, y, cv=KFold(n_splits=n_splits)))
    
            cv_accs[seed, i, j] = cv_acc
        
cv_means = np.mean(cv_accs, axis=0)
cv_stds = np.std(cv_accs, axis=0)

fig, axs = plt.subplots(ncols=2, layout='constrained', figsize=(6, 4))

axs[0].plot(folds, cv_means.T, '.-', label=neighbors, clip_on=False)
axs[0].legend(title='n_neighbors', frameon=False)
axs[0].set_ylabel('Mean CV estimate')
axs[0].set_xlabel('Number of folds (n_splits)')
axs[0].set_xlim([0, 100])
axs[0].spines[['right', 'top']].set_visible(False)

axs[1].plot(folds, cv_stds.T, '.-', label=neighbors, clip_on=False)
axs[1].legend(title='n_neighbors', frameon=False)
axs[1].set_ylabel('Standard deviation of CV estimates')
axs[1].set_xlabel('Number of folds (n_splits)')
axs[1].set_xlim([0, 100])
axs[1].spines[['right', 'top']].set_visible(False)

fig.suptitle('KNeighborsClassifier performance estimated via KFold\n'
            'on make_blobs(n_samples=100, centers=5, n_features=2)\n'
            'Mean and standard deviation over 1000 datasets');

fig.savefig('kfold.png', dpi=100, facecolor='w')
```

Labels: New Feature