2016-11-19 50 views
0

我有以下代碼用於懶惰地計算python 3.5中的值。我也嘗試了@cached_propertydecorator,結果相同,所以我會用它來簡化。如何避免在Python中傳遞作爲參數的惰性屬性評估

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

如果可能,我想讓該屬性更懶,因爲它只應在實際閱讀時進行評估。

+1

使其懶惰是讓它的唯一辦法一個'bar'知道在必要時調用的函數。函數參數總是立即評估。 – chepner

回答

4

Python在您尋求的級別上缺乏簡單,慣用的懶惰屬性評估。

有幾種方法可以獲得像這樣的懶惰屬性評估,但它們涉及被調用函數的參與和合作(bar)。例如。你可以傳遞一個對象和屬性名

def bar2(x, y, propname): 
    if x != 10: 
     print(x) 
    else: 
     print(getattr(y, propname)) 

bar2(3, foo1, 'expensive_object') 

或者你可以在調用傳似拉姆達:

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,在需要的時候。在使用功能還是有合作,但是這使得當你需要做出評價懶惰你在一個靜態值傳遞的時候,你的願望,或λ:

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! 
+0

我希望找到一種方法來區分屬性,如果被通過或呼叫,但我現在意識到這是不可能的。 我會解決一個吸氣功能。 這些都是很好的例子! – mvbentes