我最近尝试了google foo.bar challenge。在我的时间结束后,我决定尝试找到一个解决我不能做的问题的方法,并找到了一个解决方案here (如果你感兴趣的话,包括问题陈述)。我以前一直在为我想缓存的每个函数编写一个字典,但是在这个解决方案中,任何函数/输入都可以使用相同的语法进行缓存。在
首先,我对代码是如何工作的感到困惑,*args变量没有作为参数输入(并且打印为空)。下面是一个修改过的最小示例来说明我的困惑:
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功能,但需要打印。函数示例返回memoize(n,一个lambda函数)。函数longlun只是一个具有大量无用计算的身份函数,因此很容易看出缓存是否在工作(示例(2)第一次大约需要5秒,之后几乎是瞬间)。在
以下是我的困惑:
作为奖励,有人知道如何使用装饰器来记忆函数吗?在
我想不出一个更具描述性的标题,编辑欢迎。谢谢。在
符号^{} 表示可变数量的位置参数。例如,
print
可以用作print(1)
、print(1, 2)
、print(1, 2, 3)
等等。类似地,**kwargs
表示可变数量的关键字参数。在请注意,名称}符号使它们具有变化性。在
args
和kwargs
只是一种约定,*
和{无论如何,
memoize
使用它基本上接受任何输入到func。如果未缓存func的结果,则使用参数调用它。在函数调用中,*args
基本上与函数定义中的*args
相反。例如,以下是等效的:如果
args
为空,那么调用print(*args)
与调用print()
是一样的-没有参数传递给它。在函数和lambda函数在python中是相同的。它只是创建一个函数对象的不同符号。在
问题是在
^{pr2}$example2
中,没有传递函数。你调用一个函数,然后传递它的结果。相反,必须分别传递函数及其参数。在{cd7>为什么现在有一个单独的实现^是空的?在
空的
args
来自lambda的定义。为了清楚起见,我们把它写成一个函数:请注意
nonlambda
如何获取无参数。参数n
是从包含范围作为闭包bound from the containing scope绑定的。因此,您不必将它传递给memoize—它已经绑定在nonlambda
中。因此,args
在memoize中是空的,即使longrun
确实收到了一个参数,因为两者不直接交互。现在,为什么是}?这实际上是一个稍微有点错误的问题;正确的问题是“为什么它不
mem[key] = f(*args)
,而不是{mem[f, args] = f(*args)
?”。在记忆是有效的,因为同一个函数的相同输入会产生相同的输出。也就是说,
f, args
标识您的输出。理想情况下,您的key
应该是f, args
,因为这是唯一相关的信息。在问题是你需要一种方法来查找}在
f
和{mem
内。如果您曾经尝试过将list
放入dict
中,您就会知道有些类型在映射(或任何其他合适的查找结构)中不起作用。因此,如果定义key = f, args
,则无法记住具有可变/不可损坏类型的函数。Python的functools.lru_cache
实际上有这个限制。在定义一个显式的
key
是解决这个问题的一种方法。它的优点是调用者可以选择一个适当的键,例如使用n
而不做任何修改。这提供了最佳的优化潜力。但是,它很容易被破坏-仅仅使用n
就忽略了实际调用的函数。用相同的输入记住第二个函数会破坏缓存。在有其他方法,每种方法都有利弊。常见的是类型的显式转换:},},依此类推。这是缓慢的,但最精确。另一种方法是只调用
list
到{set
到{str
或repr
,就像key = repr((f, args, sorted(kwargs.items())))
,但它依赖于每个值都有一个适当的repr
。相关问题 更多 >
编程相关推荐