99 lines
3.4 KiB
Python
99 lines
3.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Author: Taylor Smith <taylor.smith@alkaline-ml.com>
|
|
#
|
|
# Metaestimators for the ARIMA class. These classes are derived from the
|
|
# sklearn metaestimators, but adapted for more specific use with pmdarima.
|
|
|
|
from operator import attrgetter
|
|
from functools import update_wrapper
|
|
|
|
__all__ = [
|
|
'if_has_delegate'
|
|
]
|
|
|
|
|
|
class _IffHasDelegate(object):
|
|
"""Implements a conditional property using the descriptor protocol.
|
|
Using this class to create a decorator will raise an ``AttributeError``
|
|
if none of the delegates (specified in ``delegate_names``) is an attribute
|
|
of the base object or the first found delegate does not have an attribute
|
|
``attribute_name``.
|
|
|
|
This allows ducktyping of the decorated method based on
|
|
``delegate.attribute_name``. Here ``delegate`` is the first item in
|
|
``delegate_names`` for which ``hasattr(object, delegate) is True``.
|
|
|
|
See https://docs.python.org/3/howto/descriptor.html for an explanation of
|
|
descriptors.
|
|
"""
|
|
def __init__(self, fn, delegate_names):
|
|
self.fn = fn
|
|
self.delegate_names = delegate_names
|
|
|
|
# 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.
|
|
for delegate_name in self.delegate_names:
|
|
try:
|
|
attrgetter(delegate_name)(obj)
|
|
except AttributeError:
|
|
continue
|
|
else:
|
|
break
|
|
else:
|
|
attrgetter(self.delegate_names[-1])(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_has_delegate(delegate):
|
|
"""Wrap a delegated instance attribute function.
|
|
|
|
Creates a decorator for methods that are delegated in the presence of a
|
|
results wrapper. This enables duck-typing by ``hasattr`` returning True
|
|
according to the sub-estimator.
|
|
|
|
This function was adapted from scikit-learn, which defines
|
|
``if_delegate_has_method``, but operates differently by injecting methods
|
|
not based on method presence, but by delegate presence.
|
|
|
|
Examples
|
|
--------
|
|
>>> from pmdarima.utils.metaestimators import if_has_delegate
|
|
>>>
|
|
>>> class A(object):
|
|
... @if_has_delegate('d')
|
|
... def func(self):
|
|
... return True
|
|
>>>
|
|
>>> a = A()
|
|
>>> # the delegate does not exist yet
|
|
>>> assert not hasattr(a, 'func')
|
|
>>> # inject the attribute
|
|
>>> a.d = None
|
|
>>> assert hasattr(a, 'func') and a.func()
|
|
|
|
Parameters
|
|
----------
|
|
delegate : string, list of strings or tuple of strings
|
|
Name of the sub-estimator that can be accessed as an attribute of the
|
|
base object. If a list or a tuple of names are provided, the first
|
|
sub-estimator that is an attribute of the base object will be used.
|
|
"""
|
|
if isinstance(delegate, list):
|
|
delegate = tuple(delegate)
|
|
if not isinstance(delegate, tuple):
|
|
delegate = (delegate,)
|
|
|
|
return lambda fn: _IffHasDelegate(fn, delegate)
|