Dictionary installation lazily

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

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

I would like these values โ€‹โ€‹to be computed when accessing the keys:

 from mysettings import settings # settings is only "prepared" print settings['expensive1'] # Now the value is really computed. 

Is it possible? How?

+6
source share
4 answers

If you do not separate the arguments from the called, I do not think this is 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 can also cache expensive_to_compute results if they are available multiple times. Something like that

 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 can also override other dict methods, depending on how you want to use the dict.

+4
source

Do not inherit the built-in dict. Even if you overwrite the dict.__getitem__() dict.get() , dict.get() will not work as you expected.

The correct 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/ligyxy/9b50bb8537069b4e154fec41a4b5995a

+1
source

You can make the expensive_to_compute generator function:

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

Then try:

 from mysettings import settings print next(settings['expensive1']) 
0
source

Save function references as values โ€‹โ€‹for ie keys:

 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 the key when you access it and call it. A suitable class that can handle values โ€‹โ€‹without laziness would be:

 import types class LazyDict(dict): def __getitem__(self,key): item = dict.__getitem__(self,key) if isinstance(item,types.FunctionType): return item() else: return item 

using:

 settings = LazyDict([("A",A),("B",B)]) print(settings["A"]) >>> that took ages 
0
source

Source: https://habr.com/ru/post/945491/


All Articles