Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

LRU + Time Expiration Decorator

00:00 In the previous lesson, I went off on a tangent and showed you how to write a decorator that takes arguments. In this lesson, I’ll be putting that into practice by writing a new LRU decorator that has time-based expiration.

00:15 Some data is time sensitive, and you don’t want to return it if it isn’t valid anymore. Think back to my example of a news site on the Web. The cache is handy for the logo, but if it caches the headline for too long, you might be reading about the Leafs winning the Cup. Okay, that’s a very specific hockey reference, and given the Web wasn’t invented the last time they won the Cup, it is impossible. Data could be stale is my point. Anyhow, to fix this, you want to include both LRU info as well as time-based info in your caching policy. Let’s go write some code that does just that.

00:54 Remember, decorators are just functions. You can call them and reuse them like any other function. Here, I’m doing just that. This is my @expiring_lru_cache decorator.

01:05 Instead of writing my own LRU implementation, here I’m using the LRU call as a function. Notice how it gets invoked. The maxsize argument goes first, then in a separate set of parentheses, the function being wrapped is given.

01:21 Think back to the previous lesson. When you’re implementing a decorator, its argument is the thing being wrapped. When you’re implementing a decorator with arguments, the argument is the decorator’s argument.

01:33 Then an inner is used. That inner function’s argument is the function to be wrapped. This double use of parentheses calls both the outer and inner functions.

01:45 As I don’t need to store a reference to the thing being wrapped, I just replaced the thing being wrapped with the wrapped thing. Yeah, that was clear. When this line is called, the original function will be replaced with the wrapped function and the func() will point the wrapped thing.

02:04 Remember how @lru_cache added a .CacheInfo() method to the function being wrapped? It could do that because functions are objects. Well, I’m doing the same thing here. Instead of adding a method, I’m adding an attribute. That attribute is the lifetime of the cache as a timedelta.

02:23 I then do that same trick to store the current time, plus the expiration time. That is when the cache becomes stale. All of this is even before the pre-condition of the function invocation.

02:36 This is getting called when everything is being wrapped, which is before the function has been called. So, a little further down, and now this is where you’re actually inside the wrapper.

02:49 These lines are the pre-condition. They’ll get called before each invocation of the wrapped function. The pre-condition checks whether the cache has expired. If it has, then it uses the LRU cache’s .cache_clear() method and sets a new expiration timestamp. After this, the actual LRU function gets invoked.

03:11 There’s no post-condition in this use case. That was a lot. Let’s go see how it gets used. Let me scroll down here.

03:22 For old time’s sake, I’ve switched back to fibonacci(). This, time I’ve decorated him with the new @expiring_lru_cache decorator.

03:31 I’ve passed in an argument of 4 seconds with the cache’s lifetime. And as I haven’t specified it, the default cache maxsize will be used.

03:40 The only new thing down here is I’ve added a sleep statement into the for loop. How long to sleep will be based on which number is generated.

03:48 The more numbers generated, the longer the nap. You can probably see where this is going. Eventually, the nap will be long enough that the cache will expire. All right, off to the command line.

04:05 There’s the first, with a bit of a delay. And the delays are getting longer … and longer. And there it is. After item three was calculated, the delay was too long.

04:20 The cache got cleared, and everything had to be recalculated. Same goes for the last one. The delay will cause the expiration to kick in from here on.

04:32 The last lesson is next. I’ll summarize the course and point you at some areas for further investigation.

Become a Member to join the conversation.