我有以下代码来懒惰地计算python 3.5中的值。我也尝试了同样结果的@cached_property
decorator,所以为了简单起见,我会使用它。
class Foo:
@property
def expensive_object(self):
if not hasattr(self, "_expensive_object"):
print("Lengthy initialization routine")
self._expensive_object = 2
return self._expensive_object
问题是,当我将它作为参数传递给函数时,它会被评估,即使它最终没有在内部使用,如下例所示:
def bar(some_parameter, another_parameter):
if some_parameter != 10:
print(some_parameter)
else:
print(another_parameter)
从下面的输出中我们看到它只是通过传递来评估,但它并不是绝对必要的,因为代码没有尝试使用它。
In [23]: foo1 = Foo()
...: bar(3, foo1.expensive_object)
Lengthy initialization routine
3
In [24]: bar(3, foo1.expensive_object)
3
在某些情况下,我的脚本可以在不需要评估的情况下运行,但由于这种情况,它最终还是会这样做。
将参数分解出来也是不切实际的。我也在组成成员对象的__init__
中使用它。
如果可能的话,我想让这个属性更加懒惰,因为只有在实际阅读时它才能进行评估。
答案 0 :(得分:4)
Python在您寻求的级别缺乏简单,惯用的懒惰属性评估。
有几种方案可以像这样进行惰性属性评估,但它们涉及被调用函数(bar
)的参与和合作。例如。你可以传入一个对象和一个属性名称
def bar2(x, y, propname):
if x != 10:
print(x)
else:
print(getattr(y, propname))
bar2(3, foo1, 'expensive_object')
或者你可以像lambda一样传入一个可调用的东西:
def bar3(x, y):
if x != 10:
print(x)
else:
print(y())
bar3(3, lambda: foo1.expensive_object)
但是尽管如此,Python本身就是一种非常简单的语言。
它并没有做很多不做评估 - 如果你不需要 - 它
甚至最简单的C或Java编译器的优化
经常做。它并没有维持几乎形而上学的左值/右值
你在Perl中看到的区别(这在这里真的很有帮助)。
并且它不会尝试动态插入和评估
thunks延迟属性调用。
当您致电foo1.expensive_object
时,它会计算该值并且
交给我。如果你希望它以不同的方式完成,你将不得不做出来
重要的其他安排,例如上文。
如果您经常需要延迟/延迟评估,那么定义一个必要的评估帮助函数会很方便:
def get_value(x):
return x() if hasattr(x, '__call__') else x
这样,在需要时,您可以在某种程度上规范评估y。使用函数仍然需要合作,但是这允许您在需要时传递静态值,或者当您需要进行评估时传递lambda:
def bar4(x, y):
if x != 10:
print(x)
else:
print(get_value(y))
bar4(3, 33) # works
bar4(4, lambda: foo1.expensive_object) # also works!