2014-07-17 16 views
2

數據我有一個像下面的自定義類。這個想法,正如命名所暗示的,我想在解析器類型的工具中評估一個令牌流。一旦一堆構造被解析出來並放入數據結構中,特定的標記序列將評估爲int,但是當數據結構尚不可用時,函數只會返回None。這個單一的複雜數據結構「構造」在程序中幾乎遍佈各處。推遲在自定義對象的計算,直到可用

class Toks(list): 
    def __init__(self, seq, constructs=Constructs()): 
     list.__init__(self, seq) 
     self.constructs = constructs 
    @property 
    def as_value(self): 
     val = tokens_as_value(self, self.constructs) 
     return val if val is not None else self 

在代碼中的點,我想這也許算的值賦給一個名稱,例如:

mything.val = Toks(tokens[start:end], constructs).as_value 

好,這給mything.val爲實際的int值或一個有趣的事情,允許我們稍後計算一個值。但是這需要一個傳球后實際執行的計算,類似於:因爲它發生

if not isinstance(mything.val, int): 
    mything.val = mything.val.as_value 

,我可以在我的程序做到這一點。然而,我真正想要發生的是完全避免第二遍,並且只需訪問該屬性就可以執行計算,並在計算出的值可以計算的情況下給出計算值(如果不可行,也可以評估爲一些主函)計算)。

任何想法?


澄清:根據不同的情況,我獲得「價值」的不同;實際的代碼更像是:

if tok.type == 'NUMBER': 
    mything.val = tok.value # A simple integer value 
else: 
    mything.val = Toks(tokens[start:end], constructs).as_value 

還有其他的情況下,有時我知道,我早知道實際的價值,有時候我不知道我是否會後來才知道的。

我知道我可以推遲調用(有點更加緊湊比@dana建議)有:

return val if val is not None else lambda: self.as_value 

然而,這使得以後的訪問mything.valmything.val()之間不一致的,所以我還是要保護它用if查看使用哪種樣式。對於類型檢查後是否需要回退到mything.val.as_valuemything.val()也是同樣的不便。

回答

0

你可以很容易地做一些事情,如:

class NaiveLazy(object): 
    def __init__(self, ctor): 
     self.ctor = ctor 
     self._value = None 
    @property 
    def value(self): 
     if self._value is None: 
      self._value = ctor() 
     return self._value 
mything = NaiveLazy(lambda: time.sleep(5) and 10) 

然後一直使用mything.value(例子來演示評估):

print mything.value # will wait 5 seconds and print 10 
print mything.value # will print 10 

我已經看到了一些工具庫,創建一個特殊的對象在ctor返回None的情況下未定義。如果你最終想要延長你的代碼超越整數,你應該想想:

class Undefined(object): pass 
UNDEFINED = Undefined() 
#... 
self._value = UNDEFINED 
#... 
if self._value is UNDEFINED: self._value = ctor() 

對於示例具體爲:

def toks_ctor(seq, constructs=Constructs()): 
    return lambda l=list(seq): tokens_as_value(l, constructs) or UNDEFINED 

mything = NaiveLazy(toks_ctor(tokens[start:end], constructs)) 
0

如果你使用Python3.2 +,考慮Future對象。該工具可讓您在後臺運行任意數量的計算。你可以等待一個單一的未來完成,並使用它的價值。或者,您可以在完成時一次「流」一次結果。

+0

不幸的是,我被困在2.7.x這個項目。但是,「未來」在這裏也不是真的。這並不是說計算需要在後臺運行,而是一旦將一些各種數據填充到數據結構中,它就需要運行(在相同的線程中)。計算本身是微不足道的,例如,甚至沒有任何理由甚至要緩存它。 –

+0

我希望Future對象能夠滿足您的需求:您可以判斷它是否可計算。它被回溯到2.7:https://pypi.python.org/pypi/futures我會好奇你選擇的解決方案! – johntellsall

+0

我認爲'Future'的界面真的很優雅。從某種意義上講,我認爲我可以使用它,因爲單線程和後臺線程的整個過程只需要在需要的數據變得可用時填入值就可以利用另一個內核而不會減慢主程序的速度。但是,它並不像我在等待一些I/O,也不是等待長時間運行的計算。我所做的總是一個微不足道的計算,問題是程序的動態流程,以及哪些數據結構已經(最終依次)填充。 –

0

您可以從as_value返回一個可調用的對象,這將允許您自動檢查實際返回值。一個缺點是你需要使用mything.val()而不是mything.val

def tokens_as_value(toks, constructs): 
    if constructs.constructed: 
     return "some value" 
    else: 
     return None 

class Constructs(object): 
    def __init__(self): 
     self.constructed = False 

class Toks(list): 
    def __init__(self, seq, constructs=Constructs()): 
     list.__init__(self, seq) 
     self.constructs = constructs 

    @property 
    def as_value(self): 
     return FutureVal(tokens_as_value, self, self.constructs) 

class FutureVal(object): 
    def __init__(self, func, *args, **kwargs): 
     self.func = func 
     self._val = None 
     self.args = args 
     self.kwargs = kwargs 

    def __call__(self): 
     if self._val is None: 
      self._val = self.func(*self.args, **self.kwargs) 
     return self._val 

只是爲了示例的目的,Constructs只包含一個布爾值,指示是否真正的價值,應當看tokens_as_value返回。

用法:

>>> t = test.Toks([]) 
>>> z = t.as_value 
>>> z 
<test.FutureVal object at 0x7f7292c96150> 
>>> print(z()) 
None 
>>> t.constructs.constructed = True 
>>> print(z()) 
our value