Hashing a python function to regenerate output when the function is modified
Question: I have a python function that has a deterministic result. It takes a long time
to run and generates a large output:

    
    
    def time_consuming_function():
        # lots_of_computing_time to come up with the_result
        return the_result
    

I modify `time_consuming_function` from time to time, but I would like to
avoid having it run again while it's unchanged. [`time_consuming_function`
only depends on functions that are immutable for the purposes considered here;
i.e. it might have functions from Python libraries but not from other pieces
of my code that I'd change.] The solution that suggests itself to me is to
cache the output and also cache some "hash" of the function. If the hash
changes, the function will have been modified, and we have to re-generate the
output.

Is this possible or ridiculous?

* * *

**Updated** : based on the answers, it looks like what I want to do is to
"memoize" `time_consuming_function`, except instead of (or in addition to)
arguments passed into an invariant function, I want to account for a function
that itself will change.


Answer: If I understand your problem, I think I'd tackle it like this. It's a touch
evil, but I think it's more reliable and on-point than the other solutions I
see here.

    
    
    import inspect
    import functools
    import json
    
    def memoize_zeroadic_function_to_disk(memo_filename):
        def decorator(f):
            try:
                with open(memo_filename, 'r') as fp:
                    cache = json.load(fp)
            except IOError:
                # file doesn't exist yet
                cache = {}
    
            source = inspect.getsource(f)
    
            @functools.wraps(f)
            def wrapper():
                if source not in cache:
                    cache[source] = f()
                    with open(memo_filename, 'w') as fp:
                        json.dump(cache, fp)
    
                return cache[source]
            return wrapper
        return decorator
    
    @memoize_zeroadic_function_to_disk(...SOME PATH HERE...)
    def time_consuming_function():
        # lots_of_computing_time to come up with the_result
        return the_result
    

