【问题标题】:Python lazy computing with garbage collection带有垃圾收集的 Python 惰性计算
【发布时间】:2021-06-24 11:12:55
【问题描述】:

给定:

  1. 一组计算相互依赖的变量
  2. 从变量中选择的一组期望输出

我想:

  1. 只计算我需要的变量(惰性计算)
  2. 每个变量最多计算一次(缓存)
  3. 摆脱在输出或剩余输出的计算(垃圾收集)中不再需要的变量
  4. 奖励:对计算进行排序,以便可以首先删除要删除的最大变量,从而最大限度地减少内存使用量

例如:

    a = 1
    b = 2
    c = b ** 10
    d = a + b

在这个例子中:

  1. 如果 a 是唯一需要的输出,则永远不需要计算 bcd
  2. 如果需要cd,那么b 应该只计算一次
  3. 如果需要cd,则可以在计算出d 后立即忘记a
  4. 由于a 最终会被垃圾回收,我们尝试尽快安排,因此首先计算d(或者如果** 操作会暂时占用更多内存,我们应该从c 开始?对此不完全确定...)

在编写上述计算时,作为一个简单的语句序列,没有观察到属性 1 和 4。

同时,可以使用@property获取属性1和3(缺少2和4):

    class DataGetter:
        @property
        def a(self): return 1
        @property
        def b(self): return 2
        @property
        def c(self): return self.b ** 10
        @property
        def d(self): return self.a + self.b

同样可以使用@cached_property获取属性1和2(缺少3和4):

    class DataGetter:
        @cached_property
        def a(self): return 1
        @cached_property
        def b(self): return 2
        @cached_property
        def c(self): return self.b ** 10
        @cached_property
        def d(self): return self.a + self.b

有没有办法确保前 3 个属性都得到满足? (可能还有第四个?)

【问题讨论】:

    标签: python garbage-collection


    【解决方案1】:

    如果我们将每个变量包装在一个实例中,那么可以通过lambda:延迟计算来实现惰性,并且可以以正常方式实现缓存。由于 lambda 是闭包,因此每个都会包含“单元”,这些“单元”仅保留 lambda 实际使用的外部函数中的局部变量(请参阅 this Q&A),从而允许垃圾收集按需要进行。

    class LazyValue:
        def __init__(self, f):
            self._f = f
        @cached_property
        def value(self):
            v = self._f()
            self._f = None
            return v
    

    需要设置self._f = None 才能使垃圾收集按预期工作;如果已经计算了一个值,那么我们不需要或不想保留对 self._f 关闭的任何其他 LazyValue 实例的引用。

    用法:

    def compute():
        a = LazyValue(lambda: 1)
        b = LazyValue(lambda: 2)
        c = LazyValue(lambda: b.value ** 10)
        d = LazyValue(lambda: a.value + b.value)
        # return whichever results are required
        return d
    
    print(compute().value) # 3
    

    【讨论】:

    • 巧妙。您通常如何返回值?假设你有一个变量名列表作为输入,你会返回这样的东西吗? {k:v.value for k,v in [('a',a),('b',b),('c',c),('d',d),] if k in required_outputs}
    • P.S.看起来 LazyValue 的值可能只是 @cached_property 而不是 @property 模仿 @cached_property 的行为。这背后有什么原因吗?兼容性?
    • @LemmeTestThat 对于返回值,实际上返回未计算的变量比先计算它们更有意义;否则就不可能进行垃圾收集,因为您正在评估它们,而它们都被局部变量引用。要按名称选择一些变量,可以使用locals() 以避免重复。关于@cached_property 的好点。此外,我还对垃圾收集进行了编辑,因为我意识到 self._f 在不再需要后仍会保留引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 2022-01-11
    • 1970-01-01
    • 2012-03-13
    • 2011-07-08
    相关资源
    最近更新 更多