Python惰性计算器

Python惰性计算器,python,caching,lazy-evaluation,memoization,Python,Caching,Lazy Evaluation,Memoization,是否有一种Pythonic方式来封装惰性函数调用,即在首次使用函数f()时,它调用先前绑定的函数g(Z),并在后续调用f()时返回缓存值 请注意,回忆录可能并不完美 我有: f = g(Z) if x: return 5 elif y: return f elif z: return h(f) a = f(Z) if x: return 5 elif y: return a elif z: return h(a) 代码可以工作,但我

是否有一种Pythonic方式来封装惰性函数调用,即在首次使用函数
f()
时,它调用先前绑定的函数
g(Z)
,并在后续调用
f()
时返回缓存值

请注意,回忆录可能并不完美

我有:

f = g(Z)
if x:
     return 5
elif y:
     return f
elif z:
     return h(f)
a = f(Z)
if x:
     return 5
elif y:
     return a
elif z:
     return h(a)
代码可以工作,但我想重新构造它,以便仅在使用值时调用
g(Z)
。我不想更改
g(…)
的定义,而且
Z
的缓存有点大


编辑:我假设
f
必须是一个函数,但情况可能并非如此。

尝试使用此装饰器:

class Memoize:
    def __init__ (self, f):
        self.f = f
        self.mem = {}
    def __call__ (self, *args, **kwargs):
        if (args, str(kwargs)) in self.mem:
            return self.mem[args, str(kwargs)]
        else:
            tmp = self.f(*args, **kwargs)
            self.mem[args, str(kwargs)] = tmp
            return tmp
(摘自死链接:/) (发现于此:亚历克斯·马泰利)


编辑:这里有另一种形式的属性(使用
\uuuu get\uuuuu

有很多装饰程序用于记忆:

http://code.activestate.com/recipes/498110-memoize-decorator-with-o1-length-limited-lru-cache/

想出一个完全通用的解决方案比你想象的要难。例如,您需要注意不可散列的函数参数,并且需要确保缓存不会增长太大

如果您真的在寻找一个惰性函数调用(只有在需要值时才对函数进行实际计算),您可能会使用生成器来实现这一点

编辑:所以我猜你到底想要的是懒惰的评估。这是一个可能正是您所寻找的图书馆:


您可以使用缓存装饰器,让我们看一个例子

from functools import wraps

class FuncCache(object):
    def __init__(self):
        self.cache = {}

    def __call__(self, func):
        @wraps(func)
        def callee(*args, **kwargs):
            key = (args, str(kwargs))
            # see is there already result in cache
            if key in self.cache:
                result = self.cache.get(key)
            else:
                result = func(*args, **kwargs)
                self.cache[key] = result
            return result
        return callee
使用缓存装饰器,您可以在这里编写

my_cache = FuncCache()

@my_cache
def foo(n):
    """Expensive calculation

    """
    sum = 0
    for i in xrange(n):
        sum += i
    print 'called foo with result', sum
    return sum

print foo(10000)
print foo(10000)
print foo(1234)
正如您可以从输出中看到的那样

called foo with result 49995000
49995000
49995000

foo将只被调用一次。您不必更改函数foo的任何一行。这就是装饰师的力量。

即使在你编辑之后,以及与detly的一系列评论之后,我仍然不太明白。在第一句话中,您说对f()的第一次调用应该调用g(),但随后返回缓存的值。但是在你的评论中,你说“无论发生什么,g()都不会被调用”(我的重点)。我不知道你在否定什么:你是说永远不应该调用g()(没有什么意义;为什么g()存在?);或者g()可能会被调用,但可能不会(这仍然与第一次调用f()时调用g()相矛盾)。然后给出一个完全不涉及g()的片段,它与问题的第一句话或detly的注释线程都没有关系

如果您再次编辑它,下面是我要回复的代码片段:

我有:

f = g(Z)
if x:
     return 5
elif y:
     return f
elif z:
     return h(f)
a = f(Z)
if x:
     return 5
elif y:
     return a
elif z:
     return h(a)
代码是有效的,但我想 重新构造它,使f(Z)仅为 如果使用了该值,则调用。我不 要更改的定义 f(…),Z缓存起来有点大

如果这真的是你的问题,那么答案很简单

if x:
    return 5
elif y:
    return f(Z)
elif z:
    return h(f(Z))
这就是如何实现“仅当使用该值时才调用f(Z)”


我不完全理解“Z有点大,需要缓存”。如果你的意思是在程序执行的过程中会有太多不同的Z值,那么记忆是无用的,那么你可能不得不求助于预先计算f(Z)的所有值,并在运行时查找它们。如果你不能做到这一点(因为你不知道你的程序将遇到的Z值),那么你就回到记忆化。如果这仍然太慢,那么您唯一的选择就是使用比Python更快的东西(试试Psyco、Cython、ShedSkin或手工编码的C模块)。

我有点困惑您是寻求缓存还是延迟计算。对于后者,请查看模块。

为了完整起见,这里有一个关于我的懒惰评估器装饰器配方的链接:

是一个相当简单的惰性装饰程序,尽管它缺少使用
@functools.wrapps
(实际上返回了
惰性的一个实例以及一些其他潜在缺陷):


我不确定这就是通常所说的懒惰。称之为缓存或记忆更安全。@John Y是对的:“惰性计算”指不计算不会影响包含表达式结果的表达式的结果,例如在
f()和g()
中,如果
f()
False
,则惰性计算不会调用
g()
。这个问题与此无关。当有函数参数时,它是记忆。否则,这只是一个懒惰的函数调用。@Neil G-肯定是一个缓存的函数结果吗
g()
无论如何至少被调用一次。@详细说明了我想要的:
g()
无论如何都不会被调用。或者,如果
f(Z)
表达式比实际中的表达式长,只需使用两个独立的
if
语句,第二个嵌套在第一个的
else
子句中。我试图按照我认为的方式编写代码,因此我希望在输入
if
之前将f(Z)绑定到某个名称。如果我需要f(Z)的结果,我可以查询它的长度,使用它的值,等等。我知道只有在需要的时候才会创建它。我很确定这正是我想要的。