参考链接
今天写算法题dp的自上而下的算法的时候,看到有人用了``@functools.lru_cache这个函数。最神奇的是没用之前我的算法是超时的,用了之后居然就跑出来了
这个函数是什么?
根据官方文档,这个函数的定义如下:1
1 | 一个为函数提供缓存功能的装饰器,缓存 maxsize 组传入参数,在下次以相同参数调用时直接返回上一次的结果。用以节约高开销或I/O函数的调用时间。 |
- 取这个函数名的意义在于,LRU叫做最久未使用算法,是一种缓存文件的置换方法。这些种方法会使用最久没有被访问的对象作为置换进去的对象。于此相对的方法还有,最近最少使用,非最近使用,先进先出等等算法。
- 也就是说,这个函数默认会储存128个调用结果。这个函数实现了备忘功能,会避免在传入相同函数的重复计算。超过这个条目的缓存,会根据LRU规则被丢弃掉。可以使
cache_clear()
来清除缓存
实现场景
- 比如在一些使用recursion的场景下,会重复调用很多相同参数的函数(我的雅虎网测也是因此没有通过)。使用这种方法可以很好的减少计算量
- 比如斐波那契的计算,计算31的时候调用函数的次数已经差出了几千倍,时间也差很多
- 使用的时候记得
import functools
1
2
3
4
5
def fib_with_cache(n):
if n < 2:
return n
return fib_with_cache(n - 2) + fib_with_cache(n - 1)
什么是装饰器
既然说到了这种装饰器,我就突然发现自己还不知道什么是装饰器。
参考链接
核心思想:python万物皆对象,包括函数本身
装饰器(decorators)
- 整体来说,装饰器的作用就是在不影响以前的函数的情况下,提供我们需要的效果。本质上来说,装饰器也是一个python的函数,他可以让其他函数实现额外的功能,而不修改本身的代码。可以用装饰器实现大量复用的功能
- 装饰器返回的也是函数对象。
一个例子
如果现在有一个函数1
2def foo():
print('i am foo')
但是我想要一个需求,就是记录下来执行日志1
2
3def foo():
print('i am foo')
logging.info("foo is running")
但是如果在很多个函数中都想要实现这个功能,那么我们需要修改每个函数的内容,加上log的代码。如果这个log的功能只是测试的时候用一下,那么修改的时候还需要把所有的代码再删掉。这时候最开始的想法是写一个新的函数包括这些功能,比如1
2
3
4
5
6
7
8def foo():
print("THIS is foo")
def decotator(func):
print("start...%s"%func)
func()
decotator(foo)
但是写成这样挺丑的,而且调用这个函数的时候就变成了不是调用这个函数本身,这时候就需要装饰器来工作。
- 面函数里面的wrapper是一个函数对象,因为修饰器的参数是函数,wrapper是来解决这个函数还有参数的问题。
这个效果也就等同于
foo = Log(foo)
,然后调用的时候调用foo(arg)
的效果。把这个方法自动化之后就是加上@让他自动变成装饰器了1
2
3
4
5
6
7
8
9def Log(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running"%func.__name__)
return func(*args)
return wrapper
def foo():
print("this is foo")在装饰器调用的时候,用@来表示,避免再一次的赋值操作(本来应该是
foo = Log(foo) foo()
的这种调用方法。
在实现带参数的装饰器的时候,可以给装饰器再套一层。注意函数名带括号和不带括号的区别1
2
3
4
5
6
7
8def Log(level):
def decorator(func):
def wrapper(*args,**kwargs):
if level == "1":
logging.warn("start %s"%func.__name__)
return func(*args)
return wrapper
return decorator
其他的一些
- 类装饰器:如果装饰器的参数是个类,可以给类写装饰器,可以实现更多的功能
- 装饰器的顺序,离这个函数越近的越是
可变参数
参考
上面的参数里面有加了星标的,这个突然发现自己有一点理解不准确,而且这么久了感觉也没怎么用到过。就一起写在这里了
总得来说,可变参数就是可以处理不同数量的参数
*args
1 | def argsFunc(a, *args): |
**kwargs
- 参数名字前面加两个星号,表示参数会被存放在dict里面,这时候调用这个函数的时候需要输入
arg1 = value1, arg2 = value2
这种形式 - 这种的一般会在调用sql的时候用到