73 lines
2.3 KiB
Python
73 lines
2.3 KiB
Python
"""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__))
|