""" - the popular ``memoize_default`` works like a typical memoize and returns the default otherwise. - ``CachedMetaClass`` uses ``memoize_default`` to do the same with classes. """ NO_DEFAULT = object() def memoize_default(default=None, evaluator_is_first_arg=False, second_arg_is_evaluator=False): """ This is a typical memoization decorator, BUT there is one difference: To prevent recursion it sets defaults. Preventing recursion is in this case the much bigger use than speed. I don't think, that there is a big speed difference, but there are many cases where recursion could happen (think about a = b; b = a). """ def func(function): def wrapper(obj, *args, **kwargs): if evaluator_is_first_arg: cache = obj.memoize_cache elif second_arg_is_evaluator: # needed for meta classes cache = args[0].memoize_cache else: cache = obj._evaluator.memoize_cache try: memo = cache[function] except KeyError: memo = {} cache[function] = memo key = (obj, args, frozenset(kwargs.items())) if key in memo: return memo[key] else: if default is not NO_DEFAULT: memo[key] = default rv = function(obj, *args, **kwargs) memo[key] = rv return rv return wrapper return func class CachedMetaClass(type): """ This is basically almost the same than the decorator above, it just caches class initializations. I haven't found any other way, so I'm doing it with meta classes. """ @memoize_default(None, second_arg_is_evaluator=True) def __call__(self, *args, **kwargs): return super(CachedMetaClass, self).__call__(*args, **kwargs)