2013-05-21 2 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 импортировать настройки 'оценивает содержимое модуля и поэтому полностью создает dict. – 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 

Edit:

Вы также можете кэшировать результаты 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 методы в зависимости от того, как вы хотите использовать Dict.

+0

Сохранение функции и переписывание '__getitem__' является умным, в то время как я думаю, было бы лучше наследовать abc.Mapping вместо встроенного dict. В противном случае он не поддерживает .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. Даже если вы переписываете метод dict.__getitem__(), dict.get() не будет работать так, как вы ожидали.

Правильный способ - наследовать abc.Mapping от 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) 

Тогда вы можете сделать:

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

Я также список образец кода и примеры здесь: https://gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a

Смежные вопросы