#NEWLINE# def memoized( func = None , key_factory = equal_args , cache_factory = dict ) : #NEWLINE# #INDENT# "Memoizes the results of a function call.\n\n  By default, exactly one result is memoized for each unique combination of function arguments.\n\n  Note that memoization is not thread-safe and the default result cache will grow without bound;\n  so care must be taken to only apply this decorator to functions with single threaded access and\n  an expected reasonably small set of unique call parameters.\n\n  Note that the wrapped function comes equipped with 3 helper function attributes:\n\n  + `put(*args, **kwargs)`: A context manager that takes the same arguments as the memoized\n                            function and yields a setter function to set the value in the\n                            memoization cache.\n  + `forget(*args, **kwargs)`: Takes the same arguments as the memoized function and causes the\n                               memoization cache to forget the computed value, if any, for those\n                               arguments.\n  + `clear()`: Causes the memoization cache to be fully cleared.\n\n  :API: public\n\n  :param func: The function to wrap.  Only generally passed by the python runtime and should be\n               omitted when passing a custom `key_factory` or `cache_factory`.\n  :param key_factory: A function that can form a cache key from the arguments passed to the\n                      wrapped, memoized function; by default uses simple parameter-set equality;\n                      ie `equal_args`.\n  :param cache_factory: A no-arg callable that produces a mapping object to use for the memoized\n                        method's value cache.  By default the `dict` constructor, but could be a\n                        a factory for an LRU cache for example.\n  :raises: `ValueError` if the wrapper is applied to anything other than a function.\n  :returns: A wrapped function that memoizes its results or else a function wrapper that does this.\n  " #NEWLINE# if ( key_factory is None ) : #NEWLINE# #INDENT# return functools . partial ( memoized , key_factory = key_factory , cache_factory = cache_factory ) #NEWLINE# #UNINDENT# if ( not inspect . isfunction ( func ) ) : #NEWLINE# #INDENT# raise ValueError ( 'The @memoized decorator must be applied innermost of all decorators.' ) #NEWLINE# #UNINDENT# key_func = ( key_factory or equal_args ) #NEWLINE# memoized_results = ( cache_factory ( ) if cache_factory else { #NEWLINE# #INDENT# #NEWLINE# #UNINDENT# } ) #NEWLINE# @ functools . wraps ( func ) #NEWLINE# def memoize( * args , **kwargs ) : #NEWLINE# #INDENT# key = key_func ( * args , ** kwargs ) #NEWLINE# if ( key in memoized_results ) : #NEWLINE# #INDENT# return memoized_results [ key ] #NEWLINE# #UNINDENT# result = func ( * args , ** kwargs ) #NEWLINE# memoized_results [ key ] = result #NEWLINE# return result #NEWLINE# #UNINDENT# @ contextmanager #NEWLINE# def put( * args , **kwargs ) : #NEWLINE# #INDENT# key = key_func ( * args , ** kwargs ) #NEWLINE# ( yield functools . partial ( memoized_results . __setitem__ , key ) ) #NEWLINE# #UNINDENT# memoize . put = put #NEWLINE# def forget( * args , **kwargs ) : #NEWLINE# #INDENT# key = key_func ( * args , ** kwargs ) #NEWLINE# if ( key in memoized_results ) : #NEWLINE# #INDENT# del memoized_results [ key ] #NEWLINE# #UNINDENT# #UNINDENT# memoize . forget = forget #NEWLINE# def clear( ) : #NEWLINE# #INDENT# memoized_results . clear ( ) #NEWLINE# #UNINDENT# memoize . clear = clear #NEWLINE# return memoize