python懒惰变量?或者,延迟了昂贵的计算

kef*_*ich 13 python numpy

我有一组非常大且计算成本昂贵的数组,并且在任何给定的运行中我的代码都不一定需要这些数组.我想让他们的声明可选,但理想情况下无需重写我的整个代码.

现在的示例:

x = function_that_generates_huge_array_slowly(0)
y = function_that_generates_huge_array_slowly(1)
Run Code Online (Sandbox Code Playgroud)

我想做的例子:

x = lambda: function_that_generates_huge_array_slowly(0)
y = lambda: function_that_generates_huge_array_slowly(1)
z = x * 5 # this doesn't work because lambda is a function
      # is there something that would make this line behave like
      # z = x() * 5?
g = x * 6
Run Code Online (Sandbox Code Playgroud)

虽然如上所述使用lambda实现了所需的效果之一 - 数组的计算延迟到需要时 - 如果多次使用变量"x",则必须每次计算.我想只计算一次.

编辑:经过一些额外的搜索,看起来有可能做我想要的(大约)类中的"懒惰"属性(例如http://code.activestate.com/recipes/131495-lazy-attributes/) .我不认为没有单独的课程可以做任何类似的事情吗?

编辑2:我正在尝试实现一些解决方案,但我遇到了一个问题,因为我不明白它们之间的区别:

class sample(object):
    def __init__(self):
        class one(object):
            def __get__(self, obj, type=None):
                print "computing ..."
                obj.one = 1
                return 1
        self.one = one()
Run Code Online (Sandbox Code Playgroud)

class sample(object):
    class one(object):
        def __get__(self, obj, type=None):
            print "computing ... "
            obj.one = 1
            return 1
    one = one()
Run Code Online (Sandbox Code Playgroud)

我认为这些的一些变化是我正在寻找的,因为昂贵的变量旨在成为一个类的一部分.

agf*_*agf 7

问题的前半部分(重用值)很容易解决:

class LazyWrapper(object):
    def __init__(self, func):
        self.func = func
        self.value = None
    def __call__(self):
        if self.value is None:
            self.value = self.func()
        return self.value

lazy_wrapper = LazyWrapper(lambda: function_that_generates_huge_array_slowly(0))
Run Code Online (Sandbox Code Playgroud)

但你还是把它当作lazy_wrapper()没有lasy_wrapper.

如果您要多次访问某些变量,则使用起来可能会更快:

class LazyWrapper(object):
    def __init__(self, func):
        self.func = func
    def __call__(self):
        try:
            return self.value
        except AttributeError:
            self.value = self.func()
            return self.value
Run Code Online (Sandbox Code Playgroud)

这会使第一次调用变慢,后续使用更快.

编辑:我发现你找到了一个类似的解决方案,要求你在类上使用属性.无论哪种方式都需要你重写每个懒惰的变量访问,所以只需选择你喜欢的.

编辑2:你也可以这样做:

class YourClass(object)
    def __init__(self, func):
        self.func = func
    @property
    def x(self):
        try:
            return self.value
        except AttributeError:
            self.value = self.func()
            return self.value
Run Code Online (Sandbox Code Playgroud)

如果要x作为实例属性进行访问.不需要额外的课程.如果您不想更改类签名(通过使其需要func,您可以将函数调用硬编码到属性中).


Gri*_*ave 6

编写一个类更强大,但为了简单而优化(我认为你要求),我提出了以下解决方案:

cache = {}

def expensive_calc(factor):
    print 'calculating...'
    return [1, 2, 3] * factor

def lookup(name):
    return ( cache[name] if name in cache
        else cache.setdefault(name, expensive_calc(2)) )

print 'run one'
print lookup('x') * 2

print 'run two'
print lookup('x') * 2
Run Code Online (Sandbox Code Playgroud)


Kev*_*vin 5

Python 3.2和更高版本在functools模块中实现了LRU算法,以处理简单的缓存/存储情况:

import functools

@functools.lru_cache(maxsize=128) #cache at most 128 items
def f(x):
    print("I'm being called with %r" % x)
    return x + 1

z = f(9) + f(9)**2
Run Code Online (Sandbox Code Playgroud)