1. functools.lru_cache(maxsize=128, typed=False)
maxsize 参数指定存储多少个调用的结果。 缓存满了之后, 旧的结果会被扔掉, 腾出空间。 为了得到最佳性能, maxsize 应该设为 2 的幂,若设为 None,则可无限增长,没有限制。 typed 参数如果设为 True, 把不同参数类型得到的结果分开保存, 如:通常认为相等的浮点数和整数参数(如 1 和 1.0) 区分开。
import time
def clock(func):
def clocked(*args):
t0 = time.perf_counter()
result = func(*args)
elapsed = time.perf_counter() - t0
name = func.__name__
arg_str = ','.join(repr(arg) for arg in args)
print('[%.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
return result
return clocked
from clockdeco import clock
@clock
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
if __name__ == '__main__':
print(fibonacci(6))
[0.00000106s] fibonacci(0) -> 0
[0.00000283s] fibonacci(1) -> 1
[0.01172350s] fibonacci(2) -> 1
[0.00000142s] fibonacci(1) -> 1
[0.00000177s] fibonacci(0) -> 0
[0.00000248s] fibonacci(1) -> 1
[0.00822842s] fibonacci(2) -> 1
[0.01618506s] fibonacci(3) -> 2
[0.03584855s] fibonacci(4) -> 3
[0.00000106s] fibonacci(1) -> 1
[0.00000142s] fibonacci(0) -> 0
[0.00000283s] fibonacci(1) -> 1
[0.00792443s] fibonacci(2) -> 1
[0.01656939s] fibonacci(3) -> 2
[0.00000142s] fibonacci(0) -> 0
[0.00000248s] fibonacci(1) -> 1
[0.00798282s] fibonacci(2) -> 1
[0.00000142s] fibonacci(1) -> 1
[0.00000177s] fibonacci(0) -> 0
[0.00000248s] fibonacci(1) -> 1
[0.00798601s] fibonacci(2) -> 1
[0.01679093s] fibonacci(3) -> 2
[0.03273356s] fibonacci(4) -> 3
[0.05797551s] fibonacci(5) -> 5
[0.10186775s] fibonacci(6) -> 8
8
使用 functools.lru_cache 缓存结果
>>> @functools.lru_cache()
def t(arg):
print('run...')
return arg
>>> [t(i) for i in (1,2,3,3,2,1)]
run...
run...
run...
[1, 2, 3, 3, 2, 1]
from clockdeco import clock
import functools
@functools.lru_cache()
@clock
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
if __name__ == '__main__':
print(fibonacci(6))
[0.00000142s] fibonacci(0) -> 0
[0.00000248s] fibonacci(1) -> 1
[0.01075913s] fibonacci(2) -> 1
[0.00000531s] fibonacci(3) -> 2
[0.02018160s] fibonacci(4) -> 3
[0.00000531s] fibonacci(5) -> 5
[0.02896599s] fibonacci(6) -> 8
8
2. functools.singledispatch
functools.singledispatch 装饰器可以把整体方案 拆分成多个模块, 甚至可以为你无法修改的类提供专门的函数。 使用 @singledispatch 装饰的普通函数会变成泛函数(generic function) : 根据第一个参数的类型, 以不同方式执行相同操作的一组函数。
import html
import numbers
from functools import singledispatch
from collections import abc
@singledispatch
def htmlize(obj):
content = html.escape(repr(obj))
return '<pre>{}</pre>'.format(content)
@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)
@htmlize.register(tuple)
@htmlize.register(abc.MutableSequence)
def _(seq):
inner = '</li>\n<li>'.join(htmlize(item) for item in seq)
return '<ul>\n<li>{}</li>\n</ul>'.format(inner)
>>> from sp import htmlize
>>> htmlize({1,2,3})
'<pre>{1, 2, 3}</pre>'
>>> htmlize(abs)
'<pre><built-in function abs></pre>'
>>> htmlize('Hi \n i am tom')
'<p>Hi <br>\n i am tom</p>'
>>> htmlize(42)
'<pre>42 (0x2a)</pre>'
>>> htmlize(['alpha', 66, {3,2,1}])
'<ul>\n<li><p>alpha</p></li>\n<li><pre>66 (0x42)</pre></li>\n<li><pre>{1, 2, 3}</pre></li>\n</ul>'
>>> print(htmlize(['alpha', 66, {3,2,1}]))
<ul>
<li><p>alpha</p></li>
<li><pre>66 (0x42)</pre></li>
<li><pre>{1, 2, 3}</pre></li>
</ul>
3. 参数化的注册装饰器
registry = set()
def register(active=True):
def decorate(func):
print('running register(active=%s)->decorate(%s)'
% (active, func))
if active:
registry.add(func)
else:
registry.discard(func)
return func
return decorate
@register(active=False)
def f1():
print('running f1()')
@register()
def f2():
print('running f2()')
def f3():
print('running f3()')
>>> import registration_param
running register(active=False)->decorate(<function f1 at 0x10063c1e0>)
running register(active=True)->decorate(<function f2 at 0x10063c268>)
>>> registration_param.registry
{<function f2 at 0x10063c268>}
4. 参数化 clock 装饰器
import time
DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
def clock(fmt=DEFAULT_FMT):
def decorate(func):
def clocked(*_args):
t0 = time.time()
_result = func(*_args)
elapsed = time.time() - t0
name = func.__name__
args = ', '.join(repr(arg) for arg in _args) result = repr(_result)
print(fmt.format(**locals()))
return _result
return clocked
return decorate
@clock()
def snooze(seconds):
time.sleep(seconds)
snooze(.123)
[0.12412500s] snooze(0.123) -> None
@clock('{name}: {elapsed}s')
def snooze(seconds):
time.sleep(seconds)
snooze(.123)
snooze: 0.12414693832397461s
@clock('{name}({args}) dt={elapsed:0.3f}s')
def snooze(seconds):
time.sleep(seconds)
snooze(.123)
snooze(0.123) dt=0.124s