Python 带有垃圾收集的惰性计算
Python lazy computing with garbage collection
给定:
- 一组用于计算相互依赖变量的计算
- 从变量中选择的一组所需输出
我愿意:
- 只计算我需要的变量(惰性计算)
- 每个变量最多计算一次(缓存)
- 摆脱输出或剩余输出计算(垃圾收集)中不再需要的变量
- 奖励:对计算进行排序,以便首先删除要删除的最大变量,以将内存使用量减少到最大值
例如:
a = 1
b = 2
c = b ** 10
d = a + b
在这个例子中:
- 如果
a
是唯一需要的输出,那么 b
、c
和 d
永远不需要计算
- 如果需要
c
和d
,那么b
应该只计算一次
- 如果需要
c
和 d
,那么 a
可以在计算 d
后立即忘记
- 由于
a
最终可以被垃圾收集,我们尝试尽快安排,因此首先计算 d
(或者如果 [=28,我们应该从 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 个属性? (也可能是第 4 个?)
如果我们将每个变量包装在一个实例中,那么可以通过lambda:
延迟计算来实现惰性,并且可以以正常方式实现缓存。由于 lambda 是闭包,每个都将包含“单元格”,这些单元仅保留 lambda 实际使用的外部函数的局部变量(参见 ),从而允许垃圾收集按需工作。
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
给定:
- 一组用于计算相互依赖变量的计算
- 从变量中选择的一组所需输出
我愿意:
- 只计算我需要的变量(惰性计算)
- 每个变量最多计算一次(缓存)
- 摆脱输出或剩余输出计算(垃圾收集)中不再需要的变量
- 奖励:对计算进行排序,以便首先删除要删除的最大变量,以将内存使用量减少到最大值
例如:
a = 1
b = 2
c = b ** 10
d = a + b
在这个例子中:
- 如果
a
是唯一需要的输出,那么b
、c
和d
永远不需要计算 - 如果需要
c
和d
,那么b
应该只计算一次 - 如果需要
c
和d
,那么a
可以在计算d
后立即忘记 - 由于
a
最终可以被垃圾收集,我们尝试尽快安排,因此首先计算d
(或者如果 [=28,我们应该从c
开始=] 操作会不会暂时占用更多内存?这个不是很确定...)
在编写上述计算时,作为简单的语句序列,未观察到属性 1 和 4。
同时,可以使用 @property
:
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
:
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 个属性? (也可能是第 4 个?)
如果我们将每个变量包装在一个实例中,那么可以通过lambda:
延迟计算来实现惰性,并且可以以正常方式实现缓存。由于 lambda 是闭包,每个都将包含“单元格”,这些单元仅保留 lambda 实际使用的外部函数的局部变量(参见
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