【问题标题】:How to avoid lazy property evaluation on passing as argument in python如何避免在python中作为参数传递时进行惰性属性评估
【发布时间】:2016-11-19 02:02:35
【问题描述】:

我有以下代码用于在 python 3.5 中懒惰地计算一个值。我也尝试了@cached_propertydecorator,结果相同,所以为了简单起见,我将使用它。

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__ 中使用它。

如果可能的话,我想让该属性更加懒惰,因为它应该只在实际读取时进行评估。

【问题讨论】:

  • 让它变得更懒的唯一方法是让它成为bar知道在必要时调用的函数。函数参数总是立即计算。

标签: python lazy-evaluation


【解决方案1】:

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 进行某种程度的正则化。 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!

【讨论】:

  • 我希望找到一种方法来区分属性是否被传递或调用,但我现在意识到这不太可能。我将满足于一个吸气剂功能。这些都是很好的例子!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多