def memoizemethod(method):
 @wraps(method)
 def _wrapper(self, *args, **kwargs):
  if ('_memoized_results' not in self.__dict__):
   self._memoized_results = {}
  memoized_results = self._memoized_results
  key = (method.__name__, args, tuple(sorted(kwargs.items())))
  if (key in memoized_results):
   return memoized_results[key]
  else:
   try:
    result = method(self, *args, **kwargs)
   except KeyError as e:
    if ('__wrapped__' in str(e)):
     result = None
    else:
     raise
   if (isinstance(result, GeneratorType) or (not isinstance(result, Hashable))):
    raise TypeError("Can't memoize a generator or non-hashable object!")
   return memoized_results.setdefault(key, result)
 return _wrapper

def memoizemethod ( method ) : @ wraps ( method ) def _wrapper ( self , * args , ** kwargs ) : if ( '_memoized_results' not in self . __dict__ ) : self . _memoized_results = { } memoized_results = self . _memoized_results key = ( method . __name__ , args , tuple ( sorted ( kwargs . items ( ) ) ) ) if ( key in memoized_results ) : return memoized_results [ key ] else : try : result = method ( self , * args , ** kwargs ) except KeyError as e : if ( '__wrapped__' in str ( e ) ) : result = None else : raise if ( isinstance ( result , GeneratorType ) or ( not isinstance ( result , Hashable ) ) ) : raise TypeError ( "Can't memoize a generator or non-hashable object!" ) return memoized_results . setdefault ( key , result ) return _wrapper
decorator to cause a method to cache its results in self for each combination of inputs and return the cached result on subsequent calls .