code: def _get_base_estimators(self, X):
          
          base_estimators = []
  
          kernel_cache = {}
          kernel_fns = {}
          for i, (name, estimator) in enumerate(self.base_estimators):
              if hasattr(estimator, 'kernel') and callable(estimator.kernel):
                  if not hasattr(estimator, '_get_kernel'):
                      raise ValueError(
                          'estimator %s uses a custom kernel function, but does not have a _get_kernel method' % name)
  
                  kernel_mat = kernel_fns.get(estimator.kernel, None)
                  if kernel_mat is None:
                      kernel_mat = estimator._get_kernel(X)
                      kernel_cache[i] = kernel_mat
                      kernel_fns[estimator.kernel] = kernel_mat
  
                  kernel_cache[i] = kernel_mat
  
                  # We precompute kernel, but only for training, for testing use original custom kernel function
                  kernel_estimator = clone(estimator)
                  kernel_estimator.set_params(kernel='precomputed')
                  base_estimators.append((name, kernel_estimator))
              else:
                  base_estimators.append((name, estimator))
  
          return base_estimators, kernel_cache
docs: Takes special care of estimators using custom kernel function
  
          Parameters
          ----------
          X : array, shape = (n_samples, n_features)
              Samples to pre-compute kernel matrix from.
  
          Returns
          -------
          base_estimators : list
              Same as `self.base_estimators`, expect that estimators with custom kernel function
              use ``kernel='precomputed'``.
  
          kernel_cache : dict
              Maps estimator name to kernel matrix. Use this for cross-validation instead of `X`.