我正在尝试将一些Scala代码移植到Python项目中,我遇到了以下Scala代码:
lazy val numNonZero = weights.filter { case (k,w) => w > 0 }.keys
weights
是一个非常长的项目元组列表及其相关的概率加权。经常在此列表中添加和删除元素,但检查有多少元素具有非零概率是相对罕见的。在我移植的代码中有一些其他罕见但昂贵的操作似乎从lazy val
的使用中受益匪浅。什么是最惯用的Python方法来做类似于Scala的lazy val
?
答案 0 :(得分:6)
在Scala中,lazy val
是一个最终变量,在首次访问时评估一次,而不是在声明它时。
它本质上是一个没有参数的memoized函数。
以下是在Python中实现memoization装饰器的一种方法:
from functools import wraps
def memoize(f):
@wraps(f)
def memoized(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items()))) # make args hashable
result = memoized._cache.get(key, None)
if result is None:
result = f(*args, **kwargs)
memoized._cache[key] = result
return result
memoized._cache = {}
return memoized
以下是如何使用它。使用property
,您甚至可以删除空括号,就像Scala一样:
>>> class Foo:
... @property
... @memoize
... def my_lazy_val(self):
... print "calculating"
... return "some expensive value"
>>> a = Foo()
>>> a.my_lazy_val
calculating
'some expensive value'
>>> a.my_lazy_val
'some expensive value'
答案 1 :(得分:3)
基本上,您希望更改numNonZero
的属性访问的工作方式。 Python使用descriptor来做到这一点。特别是,请查看他们对Properties的申请。
使用它,您可以推迟计算,直到访问该属性,将其缓存以供以后使用。
答案 2 :(得分:2)
>>> weights = [(1,2), (2,0), (3, 1)]
>>> numNonZero = (k for k, w in weights if w > 0)
>>> next(numNonZero)
1
>>> next(numNonZero)
3
>>> next(numNonZero)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> next(numNonZero, -1)
-1
>>> numNonZero = (k for k, w in weights if w > 0)
>>> for k in numNonZero:
... print(k)
...
1
3
答案 3 :(得分:1)
您可以在Nullary函数上使用@functools.lru_cache(maxsize=None)
来模拟lazy val
。
Python 3.6.5 (default, Mar 30 2018, 06:41:53)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import functools
>>> import random
>>> def foo():
... @functools.lru_cache(maxsize=None)
... def bar():
... return random.random()
... return bar
...
>>> f1 = foo()
>>> f2 = foo()
>>> f1()
0.11043217592970156
>>> f1()
0.11043217592970156
>>> f2()
0.3545457696543922
>>> f2()
0.3545457696543922
答案 4 :(得分:1)
@ sam-thomson的方法的一个更简单的变体(受他的方法启发):
In [1]: class Foo:
...: def __init__(self):
...: self.cached_lazy_val=None
...: @property
...: def my_lazy_val(self):
...: if not self.cached_lazy_val:
...: print("calculating")
...: self.cached_lazy_val='some expensive value'
...: return self.cached_lazy_val
...:
In [2]: f=Foo()
In [3]: f.my_lazy_val
calculating
Out[3]: 'some expensive value'
In [4]: f.my_lazy_val
Out[4]: 'some expensive value'