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?
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With