In [3]:
import operator, sklearn
from toolz.curried import (
    compose, concatv, first, identity, keyfilter, map, merge, partial, pipe, valmap
)

In [4]:
class AttributeObject(sklearn.base.BaseEstimator):
    """Return by extensions to change the doc, repr, & call attributes        
    """
    def __init__(
        self, func, 
        callback=identity, 
        arguments=[],
        keywords={},
    ):
        self.arguments = arguments
        self.callback = callback
        self.func = func
        self.keywords = keywords
        
        
    @staticmethod
    def _call_lazy_function(func):
        """Call a function if is callable applying no arguments."""
        return func() if callable(func) else func

    def _prepare_args(self, *args):
        """Replace any string argument in the keywords with
        the respective value.  Call any value that 
        is callable, no arguments are applied
        to these function.
        """
        return pipe(
            args, 
            map(
                lambda possible_kw: 
                self.keywords
                if possible_kw in self.keywords 
                else possible_kw
            ),
            map(self._call_lazy_function),
            list,
        )
    def _prepare_kwargs(self, **kwargs):
        """Filter keywords with the function arguments.  Call
        any value that is callable, no arguments are applied
        to these function.
        """
        return valmap(
            self._call_lazy_function, 
            merge(
                keyfilter(partial(operator.contains, self.arguments), self.keywords),
                kwargs,
            )
        )

    def __call__(self, *args, **kwargs):
        """Compose the function then prepare its arguments finally
        call it."""
        return compose(self.callback, self.func)(
                *self._prepare_args(*args), 
                **self._prepare_kwargs(**kwargs), 
            )
    
    def __repr__(self):
        """A custom repr."""
        try:
            return self.func.__doc__
        except:
            return """
            The method {} can accept {} as key arguments.
            """.format(self.func, self.arguments)
        
    def __getattribute__(self, attr):
        """Allow additional attributes to be accessed."""
        try:
            return super().__getattribute__(attr)
        except AttributeError:
            return AttributeObject(
                func=getattr(self.func, attr),
                arguments=self.arguments,
                keywords=self.keywords,
            )
        
        
    def __dir__(self):
        """Extend the predicted attributes."""
        return concatv(super().__dir__(), dir(self.func))

In [ ]: