mvb*_*tes 1 python lazy-evaluation
我有以下代码用于在python 3.5中延迟计算值。我也尝试过用@cached_property
装饰器获得相同的结果,因此为了简单起见,我将使用它。
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
Run Code Online (Sandbox Code Playgroud)
问题在于,即使我最终没有在内部使用它,当我将其作为函数的参数传递给它时,它也会被评估,如以下示例所示:
def bar(some_parameter, another_parameter):
if some_parameter != 10:
print(some_parameter)
else:
print(another_parameter)
Run Code Online (Sandbox Code Playgroud)
从下面的输出中,我们看到它只是通过传递而得到评估的,但是由于代码没有尝试使用它,因此它不是严格必需的。
In [23]: foo1 = Foo()
...: bar(3, foo1.expensive_object)
Lengthy initialization routine
3
In [24]: bar(3, foo1.expensive_object)
3
Run Code Online (Sandbox Code Playgroud)
在某些情况下,我的脚本可以运行而无需评估,但由于这种情况,最终还是要这样做。排除参数也是不实际的。我还在__init__
组合成员对象中使用它。
如果可能的话,我想使该属性更加懒惰,因为它仅在实际读取时才进行评估。
Python缺乏您寻求的简单,惯用的惰性属性评估。
像这样有几种获取惰性属性的方案,但是它们涉及被调用函数(bar
)的参与和配合。例如,您可以传递一个对象和一个属性名称
def bar2(x, y, propname):
if x != 10:
print(x)
else:
print(getattr(y, propname))
bar2(3, foo1, 'expensive_object')
Run Code Online (Sandbox Code Playgroud)
或者,您也可以像lambda这样传递可调用项:
def bar3(x, y):
if x != 10:
print(x)
else:
print(y())
bar3(3, lambda: foo1.expensive_object)
Run Code Online (Sandbox Code Playgroud)
但是,尽管有很多改进,但Python本质上还是一种非常简单的语言。即使不需要最简单的C或Java编译器,它也不会做很多不需要评估的优化。它不能保持您在Perl中看到的几乎形而上的左值/右值区别(在这里确实很有用)。而且,它不会尝试动态插入和评估
thunk以延迟属性调用。调用时foo1.expensive_object
,它将计算该值并将其移交给他。如果您希望以其他方式进行操作,则必须做出其他重要的安排,例如以上所述。
如果您通常需要延迟/惰性评估,可以方便地定义“必要时评估”辅助功能:
def get_value(x):
return x() if hasattr(x, '__call__') else x
Run Code Online (Sandbox Code Playgroud)
这样,您可以在需要时稍微规范化y。using函数仍然必须配合使用,但是这允许您在需要时传递静态值,在需要使评估变得更懒惰时传递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!
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
665 次 |
最近记录: |