import functools


def memoize(obj):
    """'Memoize' aka remember the output from a function and return that,
    rather than recalculating

    Stolen from:
    https://wiki.python.org/moin/PythonDecoratorLibrary#CA-237e205c0d5bd1459c3663a3feb7f78236085e0a_1

    do_not_memoize : bool
        IF this is a keyword argument (kwarg) in the function, and it is true,
        then just evaluate the function and don't memoize it.
    """
    cache = obj.cache = {}

    @functools.wraps(obj)
    def memoizer(*args, **kwargs):
        if 'do_not_memoize' in kwargs and kwargs['do_not_memoize']:
            return obj(*args, **kwargs)
        key = str(args) + str(kwargs)
        if key not in cache:
            cache[key] = obj(*args, **kwargs)
        return cache[key]

    return memoizer