简体   繁体   English

Python 相当于 Scala 的惰性 val

[英]Python equivalent of Scala's lazy val

I'm currently trying to port some Scala code to a Python project and I came across the following bit of Scala code:我目前正在尝试将一些 Scala 代码移植到 Python 项目中,但我遇到了以下 Scala 代码:

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

weights is a really long list of tuples of items and their associated probability weighting. weights是一个非常长的项目元组列表及其相关的概率权重。 Elements are frequently added and removed from this list but checking how many elements have a non-zero probability is relatively rare.经常在此列表中添加和删除元素,但检查有多少元素具有非零概率相对较少。 There are a few other rare-but-expensive operations like this in the code I'm porting that seem to benefit greatly from usage of lazy val .在我移植的代码中,还有一些其他罕见但昂贵的操作,这些操作似乎从使用lazy val受益匪浅。 What is the most idiomatic Python way to do something similar to Scala's lazy val ?执行类似于 Scala 的lazy val的最惯用的 Python 方法是什么?

In Scala, lazy val is a final variable that is evaluated once at the time it is first accessed, rather than at the time it is declared.在 Scala 中, lazy val是一个最终变量,它在第一次访问时被评估一次,而不是在它被声明的时候。 It is essentially a memoized function with no arguments.它本质上是一个没有参数的记忆函数。 Here's one way you can implement a memoization decorator in Python:这是在 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

Here's how it can be used.这是它的使用方法。 With property you can even drop the empty parentheses, just like Scala:使用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'

Essentially, you want to change how attribute access works for numNonZero .本质上,您希望更改numNonZero属性访问的工作numNonZero Python does that with a descriptor . Python 用一个descriptor 来做到这一点。 In particular, take a look at their application to Properties .特别是,看看它们对Properties的应用。

With that, you can defer calculation until the attribute is accessed, caching it for later use.有了它,您可以推迟计算,直到访问该属性,缓存它以备后用。

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 Python 教程:生成器表达式

You can use @functools.lru_cache(maxsize=None) on a nullary function to emulate a lazy val .您可以在 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

A simpler variant of @sam-thomson's approach (inspired by his approach): @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'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM