content: """Utilities for meta-estimators"""
  # Author: Joel Nothman
  #         Andreas Mueller
  # Licence: BSD
  
  from operator import attrgetter
  from functools import update_wrapper
  
  
  __all__ = ['if_delegate_has_method']
  
  
  class _IffHasAttrDescriptor(object):
      """Implements a conditional property using the descriptor protocol.
  
      Using this class to create a decorator will raise an ``AttributeError``
      if the ``attribute_name`` is not present on the base object.
  
      This allows ducktyping of the decorated method based on ``attribute_name``.
  
      See https://docs.python.org/3/howto/descriptor.html for an explanation of
      descriptors.
      """
      def __init__(self, fn, attribute_name):
          self.fn = fn
          self.get_attribute = attrgetter(attribute_name)
          # update the docstring of the descriptor
          update_wrapper(self, fn)
  
      def __get__(self, obj, type=None):
          # raise an AttributeError if the attribute is not present on the object
          if obj is not None:
              # delegate only on instances, not the classes.
              # this is to allow access to the docstrings.
              self.get_attribute(obj)
          # lambda, but not partial, allows help() to work with update_wrapper
          out = lambda *args, **kwargs: self.fn(obj, *args, **kwargs)
          # update the docstring of the returned function
          update_wrapper(out, self.fn)
          return out
  
  
  def if_delegate_has_method(delegate):
      """Create a decorator for methods that are delegated to a sub-estimator
  
      This enables ducktyping by hasattr returning True according to the
      sub-estimator.
  
      >>> from sklearn.utils.metaestimators import if_delegate_has_method
      >>>
      >>>
      >>> class MetaEst(object):
      ...     def __init__(self, sub_est):
      ...         self.sub_est = sub_est
      ...
      ...     @if_delegate_has_method(delegate='sub_est')
      ...     def predict(self, X):
      ...         return self.sub_est.predict(X)
      ...
      >>> class HasPredict(object):
      ...     def predict(self, X):
      ...         return X.sum(axis=1)
      ...
      >>> class HasNoPredict(object):
      ...     pass
      ...
      >>> hasattr(MetaEst(HasPredict()), 'predict')
      True
      >>> hasattr(MetaEst(HasNoPredict()), 'predict')
      False
      """
      return lambda fn: _IffHasAttrDescriptor(fn, '%s.%s' % (delegate, fn.__name__))
  