Python相当于Scala的懒惰val

时间:2013-08-09 08:35:04

标签: python scala

我正在尝试将一些Scala代码移植到Python项目中,我遇到了以下Scala代码:

  lazy val numNonZero = weights.filter { case (k,w) => w > 0 }.keys

weights是一个非常长的项目元组列表及其相关的概率加权。经常在此列表中添加和删除元素,但检查有多少元素具有非零概率是相对罕见的。在我移植的代码中有一些其他罕见但昂贵的操作似乎从lazy val的使用中受益匪浅。什么是最惯用的Python方法来做类似于Scala的lazy val

5 个答案:

答案 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)

Generator expression

>>> 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

Python tutorial: Generator expressions

答案 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'