Skip to main content
 首页 » 编程设计

python之如何避免在 python 中作为参数传递时进行惰性属性评估

2025年05月04日24sky-heaven

我有以下代码用于在 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__ 中使用它。

如果可能的话,我想让这个属性更加惰性,因为它应该只在实际读取时计算。

请您参考如下方法:

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!