Test CacheOutput() class-based function decorator


In [1]:
import datetime as dt
import time
import fridge

Make an expensive function and time it


In [2]:
# This decorator just displays how long a function takes to run
def showtime(func):    
    def wrapper(*args,**kwargs):
        start = dt.datetime.now()
        result = func(*args,**kwargs)
        stop = dt.datetime.now()
        print( "Elapsed time:", stop - start )
        return result
    return wrapper

# Make a potentially expensive function.
# Decorate it with @showtime so it will time itself.
@showtime
def power_tower(x,N):
    for n in range(N):
        x *= x
    return x

# How long does it take to run?
big_number_1 = power_tower(2,25)
big_number_2 = power_tower(2,25)
big_number_1 == big_number_2


Elapsed time: 0:00:00.162238
Elapsed time: 0:00:00.183829
Out[2]:
True

Cache the function results

If the exact same inputs are used again before the time limit expires, then we should see the same outputs without re-calculating anything.


In [3]:
# Now make a cached version. Is it faster?
@showtime
@fridge.CacheOutput(seconds=5)
def cached_power_tower(x,N):
    for n in range(N):
        x *= x
    return x

big_number_1 = cached_power_tower(2,25)
big_number_2 = cached_power_tower(2,25)
big_number_1 == big_number_2


Elapsed time: 0:00:00.173745
Elapsed time: 0:00:00.000025
Out[3]:
True

Does the cache expire like it should?


In [4]:
# After the cache expires, this function will need time to re-calculate
time.sleep(5)
big_number_3 = cached_power_tower(2,25)


Elapsed time: 0:00:00.176798

Show help()


In [5]:
help(fridge.CacheOutput)


Help on class CacheOutput in module fridge:

class CacheOutput(builtins.object)
 |  Class-based decorator used to avoid re-calculating a function.
 |  The first time the function is called, it initializes a MiniFridge.
 |  Each time the function is called, input arguments are hashed.
 |  The resulting hash is used as a MiniFridge key, and the outputs of
 |  calling the function are stored for a limited time.
 |  
 |  Set timer using keyword arguments for datetime.timedelta:
 |  weeks, days, hours, minutes, seconds, microseconds, milliseconds
 |  
 |  Example:
 |  
 |  @CacheOutput(hours=1)
 |  def cached_power_tower(x,N):
 |      for n in range(N):
 |          x *= x
 |      return x
 |  
 |  WARNING: @CacheOutput stores *outputs* of a function.
 |  It does not replicate *side effects* of a function!
 |  
 |  Caution: Not well-tested yet, especially with multi-threading.
 |  
 |  Methods defined here:
 |  
 |  __call__(self, func, *args, **kwargs)
 |      Call self as a function.
 |  
 |  __init__(self, **kwargs)
 |      Create a MiniFridge with timer set by timedelta arguments
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)