使用*args和lambda函数在python中缓存

2024-09-27 22:23:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近尝试了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秒,之后几乎是瞬间)。在

以下是我的困惑:

  • 为什么memoize的第三个参数是空的?在memoize中打印args时,它将打印()。然而,mem[key]将func(*args)存储为func(key)?在
  • 为什么这种行为只在使用lambda函数时有效(示例将缓存,而示例2不会缓存)?我认为lambda:longrun(n)只是一种将返回longrun(n)的函数作为输入的一种短方法。在

作为奖励,有人知道如何使用装饰器来记忆函数吗?在

我想不出一个更具描述性的标题,编辑欢迎。谢谢。在


Tags: the方法lambdakey函数in示例return
1条回答
网友
1楼 · 发布于 2024-09-27 22:23:23

符号^{}表示可变数量的位置参数。例如,print可以用作print(1)print(1, 2)print(1, 2, 3)等等。类似地,**kwargs表示可变数量的关键字参数。在

请注意,名称argskwargs只是一种约定,*和{}符号使它们具有变化性。在

无论如何,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中是相同的。它只是创建一个函数对象的不同符号。在

问题是在example2中,没有传递函数。你调用一个函数,然后传递它的结果。相反,必须分别传递函数及其参数。在

^{pr2}$

{cd7>为什么现在有一个单独的实现^是空的?在

  • 空的args来自lambda的定义。为了清楚起见,我们把它写成一个函数:

    def example3(n):
        def nonlambda():
            return longrun(n)
        return memoize(n, nonlambda)
    

    请注意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到{},依此类推。这是缓慢的,但最精确。另一种方法是只调用strrepr,就像key = repr((f, args, sorted(kwargs.items()))),但它依赖于每个值都有一个适当的repr

相关问题 更多 >

    热门问题