Here s our class with a slow computation method. The slow_compute() method really doesn t do anything interesting; it just sleeps and eats up one second of time. We re going to wrap the method up with a caching decorator so that we don t have to wait the one second every time we invoke the method. Listing 6-28. import time class Foobar(object): def slow_compute(self, *args, **kwargs): time.sleep(1) return args, kwargs, 42 Now let s cache the value using a decorator function. Our strategy is that for any function named X with some argument list, we want to create a unique name and save the final computed value to that name. We want our cached value to have a human readable name, we want to reuse the original function name, as well as the arguments that were passed in the first time. Let s get to some code! Listing 6-29. import hashlib def cache(func): """ This decorator will add a _cache_functionName_HEXDIGEST attribute after the first invocation of an instance method to store cached values. """ # Obtain the function's name func_name = func.func_name # Compute a unique value for the unnamed and named arguments arghash = hashlib.sha1(str(args) + str(kwargs)).hexdigest() cache_name = '_cache_%s_%s' % (func_name, arghash) def inner(self, *args, **kwargs): if hasattr(self, cache_name): # If we have a cached value, just use it print "Fetching cached value from : %s" % cache_name return getattr(self, cache_name) result = func(self, *args, **kwargs) setattr(self, cache_name, result) return result return inner There are only two new tricks that are in this code. 1. 2. We re using the hashlib module to convert the arguments to the function into a unique single string.

Using Barcode generator for Java Control to generate, create EAN-13 Supplement 5 image in Java applications.

TarCode.com/GTIN - 13

We re using getattr, hasattr, and setattr to manipulate the cached value on the instance object. The three functions getattr, setattr, and hasattr allow you to get, set, and test for attributes on an object by using string names instead of symbols. So accessing foo.bar is equivalent to invoking

Using Barcode creation for Java Control to generate, create Barcode image in Java applications.

TarCode.com/Barcode

Using Barcode creator for Java Control to generate, create Royal Mail Barcode image in Java applications.

TarCode.com/British Royal Mail 4-State Customer Code

getattr(foo, bar ). In the previous case, we re using the attribute functions to bind the result of the slow calculation function into an attribute of an instance of Foobar. The next time the decorated method is invoked, the hasattr test will find the cached value and we return the precomputed value. Now, if we want to cache the slow method, we just throw on a @cache line above the method declaration. Listing 6-30. @cache def slow_compute(self, *args, **kwargs): time.sleep(1) return args, kwargs, 42 Fantastic! We can reuse this cache decorator for any method we want now. Let s suppose now that we want our cache to invalidate itself after every N number of calls. This practical use of currying is only a slight modification to the original caching code. The goal is the same; we are going to store the computed result of a method as an attribute of an object. The name of the attribute is determined based on the actual function name, and is concatenated with a hash string computed by using the arguments to the method. In the code sample, we ll save the function name into the variable func_name and we ll save the argument hash value into arghash. Those two variables will also be used to compute the name of a counter attribute. When the counter reaches N, we ll clear out the precomputed value so that the calculation can run again. Listing 6-31. import hashlib def cache(loop_iter): def function_closure(func): func_name = func.func_name def closure(self, loop_iter, *args, **kwargs): arghash = hashlib.sha1(str(args) + str(kwargs)).hexdigest() cache_name = '_cache_%s_%s' % (func_name, arghash) counter_name = '_counter_%s_%s' % (func_name, arghash) if hasattr(self, cache_name): # If we have a cached value, just use it print "Fetching cached value from : %s" % cache_name loop_iter -= 1 setattr(self, counter_name, loop_iter) result = getattr(self, cache_name) if loop_iter == 0: delattr(self, counter_name) delattr(self, cache_name) print "Cleared cached value" return result result = func(self, *args, **kwargs) setattr(self, cache_name, result)

Using Barcode maker for Android Control to generate, create GTIN - 128 image in Android applications.