2013-05-21 19 views
6

Powiedzmy mam ten słownik w python, zdefiniowanego na poziomie modułu (mysettings.py):Konfiguracja leniwie

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

chciałbym te wartości, które mają być wyliczony gdy klawisze są dostępne:

from mysettings import settings # settings is only "prepared" 

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

Czy to możliwe? W jaki sposób?

+0

problemem jest to, że jeśli trzymać moduł, jak to, że 'z mysettings importować ustawienia 'ocenia zawartość modułu, a zatem w pełni tworzy dict. – njzk2

Odpowiedz

4

Jeśli nie Separe argumenty od wymagalne, nie sądzę, że to możliwe. Jednak powinno to działać:

class MySettingsDict(dict): 

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


def expensive_to_compute(arg): 
    return arg * 3 

a teraz:

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

Edit:

Możesz także buforować wyniki expensive_to_compute, jeśli mają być dostępne wielokrotnie. Coś takiego

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 

A teraz:

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

Można też zastąpić inne metody w zależności od dict jak chcesz użyć dict.

+0

Przechowywanie funkcji i nadpisywanie '__getitem__' jest inteligentne, a myślę, że lepiej byłoby dziedziczyć abc.Mapping zamiast wbudowanego dict. W przeciwnym razie nie obsługuje on .get(). Możesz sprawdzić mój przykład tutaj https://gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a –

0

Można dokonać expensive_to_compute funkcję generatora:

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

Następnie spróbuj:

from mysettings import settings 

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

Interesujący pomysł, ale nie to, czego szukam. Naprawdę chciałbym, aby słownik api był nietknięty. – dangonfast

0

Przechowuj odwołań do funkcji jako wartości dla kluczy tj:

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

print(settings["A"]()) 

ten sposób, oceniasz tylko funkcję związaną z kluczem, kiedy uzyskujesz dostęp do niego i wywołujesz go. Odpowiednia klasa, która może obsłużyć posiadające wartości niż leniwe byłoby:

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

Wykorzystanie:

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

Nie dziedziczą dyktowania. Nawet jeśli nadpiszesz metodę dict.__getitem__(), dict.get() nie będzie działać zgodnie z oczekiwaniami.

Właściwym sposobem jest dziedziczenie abc.Mapping z 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) 

Następnie można zrobić:

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

ja też lista przykładowy kod i przykłady tutaj: https://gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a

Powiązane problemy