Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setup dictionary lazily

Tags:

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?

like image 693
blueFast Avatar asked May 21 '13 11:05

blueFast


2 Answers

Don't inherit build-in dict. Even if you overwrite dict.__getitem__() method, dict.get() would not work as you expected.

The right way is to inherit abc.Mapping from 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) 

Then you can do:

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

I also list sample code and examples here: https://gist.github.com/gyli/9b50bb8537069b4e154fec41a4b5995a

like image 117
Guangyang Li Avatar answered Dec 25 '22 17:12

Guangyang Li


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.

like image 41
michaelmeyer Avatar answered Dec 25 '22 18:12

michaelmeyer