Python 避免重新计算的方案

Python 避免重新计算的方案,python,performance,scala,Python,Performance,Scala,动机: 为了抽象,我有一个处理对象列表的方法。这里我展示了一个简化的版本以供说明(这里使用Python2.7): 但是,在某些情况下,输入可能是foo([obj]*1000,f),然后在函数调用中,我必须重新计算1000次f(obj)。我们可以避免它,因为所有这些都是完全相同的对象 我的解决方案: 我可以随时缓存计算结果,如下所示 def foo2(obj_lst, f): cache_map = {} def foo_single(obj): if id(obj

动机:

为了抽象,我有一个处理对象列表的方法。这里我展示了一个简化的版本以供说明(这里使用Python2.7):

但是,在某些情况下,输入可能是
foo([obj]*1000,f)
,然后在函数调用中,我必须重新计算1000次f(obj)。我们可以避免它,因为所有这些都是完全相同的对象

我的解决方案:

我可以随时缓存计算结果,如下所示

def foo2(obj_lst, f):
    cache_map = {}
    def foo_single(obj):
        if id(obj) not in cache_map:
            cache_map[id(obj)] = f(obj)
        return cache_map[id(obj)]
    result_lst = []
    for obj in obj_lst:
        result_lst.append(foo_single(obj))
    return result_lst
这正是我想要的工作,它确实可以加快重新计算的开销

我的问题:

这个解决方案对我来说不够简洁,因为我必须在每个函数中手动执行,是否有更好的解决方案来避免对非随机函数进行一般的“相同对象重新计算”?带有来自函数id和所有参数的键的全局缓存映射似乎不起作用,因为对象id仅在其生命周期内是唯一的

一般来说,我理解这在Python中可能没有太大意义,因为这些对象是可变的。我想问一下,像Scala这样的函数式编程语言中是否存在处理不可变对象问题的方案?谢谢

你在描述

这可以通过或使用您正在描述的标准库(Python 3.2+)来完成


这可以通过标准库(Python3.2+)或使用标准库(Python3.2+)

来完成,也许是这样的

 import java.util.{Collections, WeakHashMap}
 case class Memoized[T,R](f: T => R) extends Function1[T,R] {
    val mem = Collections.synchronizedMap(new WeakHashMap[T,R])
    def apply(t: T) = Option(mem.get(t)).getOrElse { 
      val r = f(t)
      mem.put(t, r)
      r
    }
 }

然后,您可以(在其他用途中)执行foo(objectswithdups,memonized(f))类似的操作,也许

 import java.util.{Collections, WeakHashMap}
 case class Memoized[T,R](f: T => R) extends Function1[T,R] {
    val mem = Collections.synchronizedMap(new WeakHashMap[T,R])
    def apply(t: T) = Option(mem.get(t)).getOrElse { 
      val r = f(t)
      mem.put(t, r)
      r
    }
 }


然后你就可以做(除了其他用途)foo(objectswithdups,memonized(f))

也许是某种函数装饰器?嗨@ScottHunter,对不起,我不太熟悉,你能给我一些提示吗?为什么你“必须在每个函数中手动完成”?你将函数作为参数传递,不是吗?哦@Dima,我是说我有很多函数做的有点类似于
foo
。也许是某种函数装饰器?嗨@ScottHunter,对不起,我不太熟悉,你能给我一些指针吗?为什么你“必须在每个函数中手动执行”?您正在将函数作为参数传递,不是吗?噢@Dima,我刚才说我有很多函数都在做类似于
foo
。太棒了!我来试试这个,好的。这是回忆录。我所做的另一个选择是使内存调用位置特定。在我们的特殊用法中,这产生了巨大的差异。我只是尝试了一下,但遇到了一些问题。有时,当我们有一个大对象时,哈希代码开销可能太大,我们想要的是它是否是内存池中的同一个对象。但是使用
id(obj)
可能会在
obj
的生命周期之外导致非唯一性问题。我们如何正确定义装饰器,使其真正反映出我们所看到的同一对象?(让我们假设对象总是不变的)显然我误解了decorator的工作原理!(更糟糕的是,也许我误解了结尾…)现在我明白这正是我想要的!太棒了!!!令人惊叹的!我来试试这个,好的。这是回忆录。我所做的另一个选择是使内存调用位置特定。在我们的特殊用法中,这产生了巨大的差异。我只是尝试了一下,但遇到了一些问题。有时,当我们有一个大对象时,哈希代码开销可能太大,我们想要的是它是否是内存池中的同一个对象。但是使用
id(obj)
可能会在
obj
的生命周期之外导致非唯一性问题。我们如何正确定义装饰器,使其真正反映出我们所看到的同一对象?(让我们假设对象总是不变的)显然我误解了decorator的工作原理!(更糟糕的是,也许我误解了结尾…)现在我明白这正是我想要的!太棒了!!!在某种程度上,是的。。。需要有一种方法来判断这两个实例是否“相同”。如果愿意,您可以在
get
put
中用
id(t)
替换
t
。Scala是否有它与python
id
的等价物?有时对象本身很大,所以计算hashcode本身不够合理……我不知道python
id
是什么,但无法想象如果不返回ref,不计算hashcode,它还能做什么。“我认为人类没有其他已知的方法。”python文档中的沈秀柳:“CPython实现细节:这是内存中对象的地址。”。因此,python中的
id(t)
与scala中的
t
几乎相同(实际上有些更糟,因为scala覆盖了许多常见类型对象的哈希和等式)。我认为,“遇到非唯一性问题”是一个相当长的镜头。这是你最不需要担心的事情。这里更值得关注的是,同一对象有许多不同的副本,具有不同的内存地址。但在函数式编程语言中,我们通常不太担心同一对象的不同副本,因为对象是不可变的,所以我们总是传递引用。在我的特定应用程序中,我们只传递引用,并保持所有内容不变,这也是事实……在某种程度上,是的。。。需要有一种方法来判断这两个实例是否“相同”。如果愿意,您可以在
get
put
中用
id(t)
替换
t
。Scala是否有它与python
id
的等价物?有时对象本身很大,所以计算hashcode本身不够合理……我不知道python
id
是什么,但无法想象如果不返回ref,不计算hashcode,它还能做什么。“我不认为人类还有任何其他已知的方法。”python文档中的沈秀柳:“CP