Here is a lazy dictionary with test cases.
"""
A dictionary whose values are only evaluated on access.
"""
from functools import partial
from UserDict import IterableUserDict
class LazyDict(IterableUserDict, object):
"""
A lazy dictionary implementation which will try
to evaluate all values on access and cache the
result for later access.
"""
def set_lazy(self, key, item, *args, **kwargs):
"""
Allow the setting of a callable and arguments
as value of dictionary.
"""
if callable(item):
item = partial(item, *args, **kwargs)
super(LazyDict, self).__setitem__(key, item)
def __getitem__(self, key):
item = super(LazyDict, self).__getitem__(key)
try:
self[key] = item = item()
except TypeError:
pass
return item
def test_lazy_dict():
"""
Simple test cases for `LazyDict`.
"""
lazy_dict = LazyDict({1: 1, 2: lambda: 2})
assert lazy_dict[2] == 2
lazy_dict[3] = 3
assert lazy_dict[3] == 3
def joiner(*args, **kwargs):
sep = kwargs.pop('sep', ' ')
kwargs = [
'%s=%s' % (k, v)
for k, v in sorted(kwargs.iteritems())]
return sep.join(list(args) + kwargs)
lazy_dict.set_lazy(
4, joiner, 'foo', 'bar', name='test', other='muah', sep=' ')
assert lazy_dict[4] == 'foo bar name=test other=muah'
assert lazy_dict.get('5') is None
# Test caching functionality.
def call_at_max(count):
counter = [0]
def inner():
counter[0] += 1
if counter[0] > count:
raise AssertionError('Called more than once')
return 'happy'
return inner
call_once = call_at_max(1)
lazy_dict[5] = call_once
assert lazy_dict[5] == 'happy'
assert lazy_dict[5] == 'happy'
# Test for helper function.
try:
call_once()
except AssertionError:
assert True
IterableUserDict
? \$\endgroup\$IterableUserDict
is a dictionary meant for subclassing that supports iteration, see docs.python.org/2/library/userdict.html for documentation. \$\endgroup\$collections.MutableMapping
instead. Note, per the docs, "The need for [UserDict
] has been largely supplanted by the ability to subclass directly from dict (a feature that became available starting with Python version 2.2)." \$\endgroup\$