Search code examples
pythonlazy-loadinglazy-evaluation

Setup dictionary lazily


Let's say I have this dictionary in python, defined at the module level (mysettings.py):

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

I would like those values to be computed when the keys are accessed:

from mysettings import settings # settings is only "prepared"

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

Is this possible? How?


Solution

  • If you don't separe the arguments from the callable, I don't think it's possible. However, this should work:

    class MySettingsDict(dict):
    
        def __getitem__(self, item):
            function, arg = dict.__getitem__(self, item)
            return function(arg)
    
    
    def expensive_to_compute(arg):
        return arg * 3
    

    And now:

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

    Edit:

    You may also want to cache the results of expensive_to_compute, if they are to be accessed multiple times. Something like this

    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
    

    And now:

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

    You may also want to override other dict methods depending of how you want to use the dict.