使用*args和lambda函数在python中进行缓存
我最近尝试了谷歌。在我的时间结束后,我决定尝试找到一个解决方案来解决我无法解决的问题,并找到了一个解决方案(如果你感兴趣,包括问题陈述)。我以前为我想要缓存的每个函数都制作了一个字典,但在这个解决方案中,任何函数/输入都可以使用相同的语法进行缓存 首先,我对代码是如何工作的感到困惑,*args变量没有作为参数输入(并且打印为空)。下面是一个经过修改的最小示例来说明我的困惑:使用*args和lambda函数在python中进行缓存,python,caching,lambda,Python,Caching,Lambda,我最近尝试了谷歌。在我的时间结束后,我决定尝试找到一个解决方案来解决我无法解决的问题,并找到了一个解决方案(如果你感兴趣,包括问题陈述)。我以前为我想要缓存的每个函数都制作了一个字典,但在这个解决方案中,任何函数/输入都可以使用相同的语法进行缓存 首先,我对代码是如何工作的感到困惑,*args变量没有作为参数输入(并且打印为空)。下面是一个经过修改的最小示例来说明我的困惑: mem = {} def memoize(key, func, *args): """ Helper t
mem = {}
def memoize(key, func, *args):
"""
Helper to memoize the output of a function
"""
print(args)
if key not in mem:
# store the output of the function in memory
mem[key] = func(*args)
return mem[key]
def example(n):
return memoize(
n,
lambda: longrun(n),
)
def example2(n):
return memoize(
n,
longrun(n),
)
def longrun(n):
for i in range(10000):
for j in range(100000):
2**10
return n
在这里,我使用相同的记忆功能,但带有打印。函数示例返回memoize(n,一个lambda函数,)。函数longrun只是一个身份函数,有很多无用的计算,因此很容易看到缓存是否工作(示例(2)第一次大约需要5秒,之后几乎是瞬间)
以下是我的困惑:
- 为什么memoize的第三个参数是空的?在中打印args时,它会打印()。然而,mem[key]以某种方式将func(*args)存储为func(key)
- 为什么此行为仅在使用lambda函数时有效(示例将缓存,但示例2不会)?我认为lambda:longrun(n)只是给出一个返回longrun(n)的函数作为输入的一种简单方法
print
可以用作print(1)
,print(1,2)
,print(1,2,3)
等等。类似地,**kwargs
表示数量可变的关键字参数
请注意,名称args
和kwargs
只是一种约定-正是*
和**
符号使它们可变
无论如何,memoize
使用它来接受func的基本输入。如果func的结果未缓存,则会使用参数调用它。在函数调用中,*args
基本上与函数定义中的*args
相反。例如,以下是等效的:
# provide *args explicitly
print(1, 2, 3)
# unpack iterable to *args
arguments = 1, 2, 3
print(*arguments)
如果args
为空,则调用print(*args)
与调用print()
相同-不向其传递任何参数
函数和lambda函数在python中是相同的。它只是创建函数对象的不同符号 问题是在
示例2中,您没有传递函数。调用一个函数,然后传递其结果。相反,您必须分别传递函数及其参数
def example2(n):
return memoize(
n,
longrun, # no () means no call, just the function object
# all following parameters are put into *args
n
)
现在,一些实现细节:为什么args
为空,为什么有一个单独的键
- 空的
args
来自您对lambda的定义。为了清晰起见,我们将其作为一个函数:
def example3(n):
def nonlambda():
return longrun(n)
return memoize(n, nonlambda)
注意nonlambda
如何不接受参数。参数n
作为闭包从包含范围绑定。因此,您不必将其传递给memoize—它已经绑定在非lambda
中。因此,args
在memoize中为空,即使longrun
确实接收到一个参数,因为这两个参数不直接交互
- 现在,为什么它是
mem[key]=f(*args)
,而不是mem[key]=f(key)
?这实际上是一个稍微有点错误的问题;正确的问题是“为什么它不是mem[f,args]=f(*args)
?”
记忆化之所以有效,是因为同一函数的相同输入导致相同的输出。也就是说,f,args
标识您的输出。理想情况下,您的键应该是f,args
,因为这是唯一相关的信息
问题是您需要一种在mem
中查找f
和args
的方法。如果您曾经尝试将列表
放在目录
中,您就会知道有些类型在映射(或任何其他合适的查找结构)中不起作用。因此,如果您定义了key=f,args
,则无法记忆采用可变/不可损坏类型的函数。Python的functools.lru\u缓存实际上有这个限制
定义显式键是解决此问题的一种方法。它的优点是调用者可以选择一个合适的键,例如在不做任何修改的情况下使用n
。这提供了最佳的优化潜力。但是,它很容易中断—仅使用n
会忽略实际调用的函数。用相同的输入记忆第二个函数会破坏缓存
有几种不同的方法,每种方法都有利弊。常见的是类型的显式转换:list
到tuple
,set
到frozenset
,等等。这很慢,但最精确。另一种方法是只调用str
或repr
,如key=repr((f,args,sorted(kwargs.items())))
,但它依赖于每个值都有一个正确的repr
符号表示数量可变的位置参数。例如,print
可以用作print(1)
,print(1,2)
,print(1,2,3)
等等。类似地,**kwargs
表示数量可变的关键字参数
请注意,名称args
和kwargs
只是一种约定-正是*
和**
符号使它们可变
无论如何,memoize
使用它来接受func的基本输入。如果func的结果未缓存,则会使用参数调用它。起作用