From fluent Python Chapter 7 function decorators and closures
Use functools lru_ Cache notes
functools.lru_cache is a very useful decorator. It implements the function of memo. This is an optimization technology, which saves the results of time-consuming functions to avoid double calculation by passing in the same parameters
clockdeco.py
import time def clock(func): def clocked(*args): t0 = time.time() result = func(*args) elapsed = time.time() - t0 name = func.__name__ arg_str = ', '.join(repr(arg) for arg in args) print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result)) return result return clocked
We use the Fibonacci sequence as an example
fibo_demo.py
from clockdeco import clock import time @clock def fibonacci(n): if n < 2: return n return fibonacci(n-2) + fibonacci(n-1) if __name__=='__main__': start = time.time() print(fibonacci(30)) print(time.time() - start)
Some outputs are as follows
... [0.92167187s] fibonacci(24) -> 46368 [1.41511607s] fibonacci(25) -> 75025 [2.26029801s] fibonacci(26) -> 121393 [3.61707902s] fibonacci(27) -> 196418 [5.80630493s] fibonacci(28) -> 317811 [9.39297986s] fibonacci(29) -> 514229 [15.82301521s] fibonacci(30) -> 832040 832040 15.82302474975586
The waste of time is obvious: fibonacci(1) called 8 times, fibonacci(5) called 5 times
Now let's add LRU to the code_ cache
fibo_demo_lru.py
import functools import time from clockdeco import clock @functools.lru_cache() # Note that LRU must be called like a regular function_ cache. There are a pair of parentheses in this line: @ functools lru_cache(). The reason for this is that lru_cache can accept configuration parameters, which will be described later. @clock # There are decorations stacked here: @lru_cache() is applied to the function returned by @ clock. In this way, the execution time is halved, and each value of n calls the function only once: def fibonacci(n): if n < 2: return n return fibonacci(n - 2) + fibonacci(n - 1) if __name__ == '__main__': start = time.time() print(fibonacci(30)) print(time.time() - start)
Some outputs are as follows
[0.00000119s] fibonacci(25) -> 75025 [0.00016594s] fibonacci(26) -> 121393 [0.00000000s] fibonacci(27) -> 196418 [0.00020003s] fibonacci(28) -> 317811 [0.00000072s] fibonacci(29) -> 514229 [0.00020981s] fibonacci(30) -> 832040 832040 0.00021791458129882812
It can be seen that LRU is used_ The cache optimized Fibonacci greatly optimizes the computing time
Special attention, lru_cache can be configured with two optional parameters. Its signature is
functools.lru_cache(maxsize=128, typed=False)
The maxsize parameter specifies how many call results are stored. When the cache is slow, the old results will be thrown away to make room. For best performance, maxsize should be set to a power of 2.
If the type parameter is set to True, the results obtained by different parameter types will be saved separately, that is, the floating-point numbers and integer parameters (such as 1 and 1.0) that are usually artificially equal will be distinguished. By the way, because LRU_ The cache uses a dictionary to store the results, and the key is created according to the location parameters and keyword parameters passed in by the call, so it is LRU_ All parameters of a cache decorated function must be hashable
Single dispatch universal function singledispatch (overload)
Suppose we are developing a tool for debugging Web applications. We want to generate HTML and display different types of Python objects. We might write such a function
import html def htmlize(obj): # The repr() function converts the object to a form that the interpreter can read content = html.escape(repr(obj)) return '<pre>{}</pre>'.format(content) print(htmlize({1,2,3}))
This function uses any Python type, but now we want to extend it to show some types in a special way
- str: replace the internal newline character with '< br > \ n'; Use < p > instead of < pre >
- int: displays numbers in decimal and hexadecimal
- List: output an HTML list and format it according to the type of each element
<pre>{1, 2, 3}</pre>
Because Python does not support overloaded methods or functions, we cannot define htmlize variants with different signatures, nor can we handle different data types in different ways
generic.py
from functools import singledispatch from collections import abc import numbers import html # @The singledispatch tag handles the base function of object type. @singledispatch def htmlize(obj): content = html.escape(repr(obj)) return '<pre>{}</pre>'.format(content) # Each special function uses @ « base_function». register(« type ») decoration. @htmlize.register(str) def _(text): content = html.escape(text).replace('\n', '<br>\n') return '<p>{0}</p>'.format(content) @htmlize.register(numbers.Integral) def _(n): return '<pre>{0} (0x{0:x})</pre>'.format(n) # Multiple register decorators can be stacked so that the same function supports different types. @htmlize.register(tuple) @htmlize.register(abc.MutableSequence) def _(seq): inner = '</li>\n<li>'.join(htmlize(item) for item in seq) return '<ul>\n<li>' + inner + '</li>\n</ul>' print(htmlize({1,2,3})) print('-'*40) print(htmlize(abs)) print('-'*40) print(htmlize('Heimlich & Co.\n- a game')) print('-'*40) print(htmlize(42)) print('-'*40) print(htmlize(['alpha', 66, {3, 2, 1}]))
The output is as follows
<pre>{1, 2, 3}</pre> ---------------------------------------- <pre><built-in function abs></pre> ---------------------------------------- <p>Heimlich & Co.<br> - a game</p> ---------------------------------------- <pre>42 (0x2a)</pre> ---------------------------------------- <ul> <li><p>alpha</p></li> <li><pre>66 (0x42)</pre></li> <li><pre>{1, 2, 3}</pre></li> </ul>