Setup dictionary lazily
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 Mappingclass 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
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.
Store references to the functions as the values for the keys i.e:
def A(): return "that took ages"def B(): return "that took for-ever"settings = { "A": A, "B": B,}print(settings["A"]())
This way, you only evaluate the function associated with a key when you access it and invoke it. A suitable class which can handle having non-lazy values would be:
import typesclass LazyDict(dict): def __getitem__(self,key): item = dict.__getitem__(self,key) if isinstance(item,types.FunctionType): return item() else: return item
usage:
settings = LazyDict([("A",A),("B",B)])print(settings["A"])>>> that took ages