我有一组非常大且计算成本昂贵的数组,并且在任何给定的运行中我的代码都不一定需要所有这些数组。我想让他们的声明可选,但理想情况下无需重写我的整个代码。
现在的示例:
x = function_that_generates_huge_array_slowly(0)
y = function_that_generates_huge_array_slowly(1)
我想做的例子:
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
虽然如上所述使用lambda实现了所需的效果之一 - 数组的计算会延迟到需要时 - 如果多次使用变量“x”,则必须每次计算。我只想计算一次。
编辑: 经过一些额外的搜索后,看起来可以在类中使用“懒惰”属性(大约)做我想要的(大约)(例如http://code.activestate.com/recipes/131495-lazy-attributes/)。我不认为没有单独的课程可以做任何类似的事情吗?
EDIT2:我正在尝试实施一些解决方案,但我遇到了一个问题,因为我不明白它们之间的区别:
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()
和
class sample(object):
class one(object):
def __get__(self, obj, type=None):
print "computing ... "
obj.one = 1
return 1
one = one()
我认为这些的一些变化是我正在寻找的,因为昂贵的变量旨在成为一个类的一部分。
答案 0 :(得分: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))
但您仍需将其用作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
这将使第一次调用变慢并且后续使用更快。
编辑:我发现您找到了一个类似的解决方案,要求您在类上使用属性。无论哪种方式都要求你重写每个懒惰的变量访问,所以只需选择你喜欢的。
编辑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
如果要将x
作为实例属性进行访问。不需要额外的课程。如果您不想更改类签名(通过使其需要func
),您可以将函数调用硬编码到属性中。
答案 1 :(得分: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
答案 2 :(得分: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
答案 3 :(得分:2)
你不能像x
这样简单的名字来真正评估懒惰。名称只是哈希表中的一个条目(例如,locals()
或globals()
返回的名称)。除非修补这些系统表的访问方法,否则不能将代码的执行附加到简单的名称解析。
但是你可以用不同的方式将函数包装在缓存包装器中。 这是一种OO方式:
class CachedSlowCalculation(object):
cache = {} # our results
def __init__(self, func):
self.func = func
def __call__(self, param):
already_known = self.cache.get(param, None)
if already_known:
return already_known
value = self.func(param)
self.cache[param] = value
return value
calc = CachedSlowCalculation(function_that_generates_huge_array_slowly)
z = calc(1) + calc(1)**2 # only calculates things once
这是一种无阶级的方式:
def cached(func):
func.__cache = {} # we can attach attrs to objects, functions are objects
def wrapped(param):
cache = func.__cache
already_known = cache.get(param, None)
if already_known:
return already_known
value = func(param)
cache[param] = value
return value
return wrapped
@cached
def f(x):
print "I'm being called with %r" % x
return x + 1
z = f(9) + f(9)**2 # see f called only once
在现实世界中,您将添加一些逻辑以将缓存保持在合理的大小,可能使用LRU算法。
答案 4 :(得分:2)
对我来说,似乎适合您的问题的解决方案是将dict子类化并使用它。
class LazyDict(dict):
def __init__(self, lazy_variables):
self.lazy_vars = lazy_variables
def __getitem__(self, key):
if key not in self and key in self.lazy_vars:
self[key] = self.lazy_vars[key]()
return super().__getitem__(key)
def generate_a():
print("generate var a lazily..")
return "<a_large_array>"
# You can add more variables as many as you want here
lazy_vars = {'a': generate_a}
lazy = LazyDict(lazy_vars)
# retrieve the variable you need from `lazy`
a = lazy['a']
print("Got a:", a)
如果您使用exec
运行代码,则可以实际上懒惰地求值。该解决方案仅使用自定义全局变量。
your_code = "print('inside exec');print(a)"
exec(your_code, lazy)
如果您做了your_code = open(your_file).read()
,则实际上可以运行代码并实现所需的功能。但是我认为更实用的方法是前一种。