2013-05-21 7 views
6

Diciamo che ho questo dizionario in python, definita a livello di modulo (mysettings.py):Setup pigramente

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

Vorrei quei valori da calcolare quando i tasti sono accessibili:

from mysettings import settings # settings is only "prepared" 

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

È possibile? Come?

+0

il problema è che se si mantiene il modulo come è, le impostazioni di 'da MySettings importano 'valuta il contenuto del modulo e quindi crea completamente il comando. – njzk2

risposta

4

Se non séparé gli argomenti dal callable, non credo che sia possibile. Tuttavia, questo dovrebbe funzionare:

class MySettingsDict(dict): 

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


def expensive_to_compute(arg): 
    return arg * 3 

E ora:

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

Edit:

Si consiglia inoltre di memorizzare nella cache i risultati di expensive_to_compute, se si vuole essere letta più volte. Qualcosa di simile

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 

E ora:

>>> 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]) 

Si potrebbe anche voler ignorare altri dict metodi a seconda di come si desidera utilizzare il dict.

+0

Memorizzare la funzione e sovrascrivere '__getitem__' è intelligente, mentre penso che sarebbe meglio ereditare abc.Mapping invece di dict incorporato. Altrimenti, non supporta .get(). Puoi controllare il mio esempio qui https://gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a –

0

È possibile effettuare expensive_to_compute una funzione di generatore:

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

Quindi provare:

from mysettings import settings 

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

Idea interessante, ma non quello che sto cercando. Mi piacerebbe davvero mantenere intatto il dizionario. – dangonfast

0

riferimenti Conservare alle funzioni dei valori per le chiavi cioè:

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

print(settings["A"]()) 

Questo modo, si valuta solo la funzione associata a un tasto quando si accede a esso e lo si richiama. Una classe adatta che può gestire avendo valori non pigri sarebbero:

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

utilizzo:

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

Non ereditare dettato incorporato. Anche se si sovrascrive il metodo dict.__getitem__(), dict.get() non funzionerebbe come previsto.

Il modo corretto è ereditare abc.Mapping da collections.

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) 

Poi si può fare:

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

Ho anche elenco dei codici di esempio e gli esempi qui: https://gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a