6

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

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

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

4

5 回答 5

8

在 Scala 中,lazy val是一个 final 变量,它在第一次访问时被评估一次,而不是在它被声明的时候。它本质上是一个没有参数的记忆函数。这是在 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'
于 2014-10-17T23:11:56.677 回答
3

本质上,您想要更改属性访问对numNonZero. Python 使用描述符来做到这一点。特别是,看看他们对Properties的应用。

这样,您可以推迟计算,直到访问该属性,将其缓存以备后用。

于 2013-08-09T09:39:34.347 回答
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

Python 教程:生成器表达式

于 2013-08-09T08:44:22.347 回答
1

您可以@functools.lru_cache(maxsize=None)在 nullary 函数上使用来模拟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
于 2018-05-16T15:47:18.443 回答
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'
于 2019-10-29T01:11:07.593 回答