2013-05-21 85 views
6

比方說,我有這個詞典在Python中,在模塊級(mysettings.py)定義:安裝字典懶洋洋地

settings = { 
    'expensive1' : expensive_to_compute(1), 
    'expensive2' : expensive_to_compute(2), 
    ... 
} 

我想這些值來計算,當按鍵被訪問:

from mysettings import settings # settings is only "prepared" 

print settings['expensive1'] # Now the value is really computed. 

這可能嗎?怎麼樣?

+0

的問題是,如果你把你的模塊是,在'從mysettings導入設置'評估模塊的內容,因此完全創建字典。 – njzk2

回答

4

如果不separe從可調用的參數,我不認爲這是可能的。然而,這應該工作:

class MySettingsDict(dict): 

    def __getitem__(self, item): 
     function, arg = dict.__getitem__(self, item) 
     return function(arg) 


def expensive_to_compute(arg): 
    return arg * 3 

現在:

>>> settings = MySettingsDict({ 
'expensive1': (expensive_to_compute, 1), 
'expensive2': (expensive_to_compute, 2), 
}) 
>>> settings['expensive1'] 
3 
>>> settings['expensive2'] 
6 

編輯:

您可能還需要緩存的expensive_to_compute的結果,如果他們要多次訪問。事情是這樣的

class MySettingsDict(dict): 

    def __getitem__(self, item): 
     value = dict.__getitem__(self, item) 
     if not isinstance(value, int): 
      function, arg = value 
      value = function(arg) 
      dict.__setitem__(self, item, value) 
     return value 

現在:

>>> settings.values() 
dict_values([(<function expensive_to_compute at 0x9b0a62c>, 2), 
(<function expensive_to_compute at 0x9b0a62c>, 1)]) 
>>> settings['expensive1'] 
3 
>>> settings.values() 
dict_values([(<function expensive_to_compute at 0x9b0a62c>, 2), 3]) 

您可能還需要重寫其他dict方法取決於你想如何使用字典。

+0

存儲函數和覆蓋__getitem__是聰明的,但我認爲繼承abc.Mapping而不是內置字典會更好。否則,它不支持.get()。你可以在這裏查看我的例子https://gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a –

0

您可以expensive_to_compute發電機功能:

settings = { 
    'expensive1' : expensive_to_compute(1), 
    'expensive2' : expensive_to_compute(2), 
} 

然後嘗試:

from mysettings import settings 

print next(settings['expensive1']) 
+0

有趣的想法,但不是我在找什麼。我真的想保持字典api不變。 – dangonfast

0

商店的函數的引用作爲鍵的值,即:

def A(): 
    return "that took ages" 
def B(): 
    return "that took for-ever" 
settings = { 
    "A": A, 
    "B": B, 
} 

print(settings["A"]()) 

這方式,當您訪問並調用它時,您只評估與某個鍵相關的功能。合適的類,它可以處理具有非延遲值是:

import types 
class LazyDict(dict): 
    def __getitem__(self,key): 
     item = dict.__getitem__(self,key) 
     if isinstance(item,types.FunctionType): 
      return item() 
     else: 
      return item 

用法:

settings = LazyDict([("A",A),("B",B)]) 
print(settings["A"]) 
>>> 
that took ages 
1

不要繼承內置字典。即使您覆蓋dict.__getitem__()方法,dict.get()也不會按預期工作。

正確的做法是從collections繼承abc.Mapping

from collections.abc import Mapping 

class LazyDict(Mapping): 
    def __init__(self, *args, **kw): 
     self._raw_dict = dict(*args, **kw) 

    def __getitem__(self, key): 
     func, arg = self._raw_dict.__getitem__(key) 
     return func(arg) 

    def __iter__(self): 
     return iter(self._raw_dict) 

    def __len__(self): 
     return len(self._raw_dict) 

然後,你可以這樣做:

settings = LazyDict({ 
    'expensive1': (expensive_to_compute, 1), 
    'expensive2': (expensive_to_compute, 2), 
}) 

我還列表示例代碼和例子在這裏:https://gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a