Skip to content

Instantly share code, notes, and snippets.

@d1manson
Last active August 29, 2015 13:58
Show Gist options
  • Save d1manson/10409675 to your computer and use it in GitHub Desktop.
Save d1manson/10409675 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 25 13:26:15 2014
@author: daniel
"""
from functools import partial
class LazyDictProperty(object):
"""
Example useage
--------------
Suppose we have a class Man::
from lazydict import LazyDictProperty
class Man:
@LazyDictProperty
def knowledge(self,key):
return "%s is in the eye of the beholder" % (key)
James = Man()
print James.knowledge["beauty"]
The idea is that in the ``knowledge`` method you will probably want to implement something
quite complicated. The benefit being that it only runs at the point you request a particular key.
For convenience, you may want to store values in the ``self.knowledge._cache`` dict.
Once you have created a LazyDictProperty, you have the opption of assigning an iterator.
To expand on the above example::
class Man:
@LazyDictProperty
def knowledge(self,key):
return "%s is in the eye of the beholder" % (key)
@knowledge.iter_function
def knowledgeIter(self):
return iter(["beauty","truth","reason"])
James = Man()
for idea in James.knowledge:
print James.knowledge[idea]
You can use round paren ``()`` to access the lazy dict instead of square ``[]``. This
is another sense in which it is lazy. In the round paren version you can pass a full
set of args and kwargs. You can also store stuff in the ._cache dictionary of the LazyDict
Implementation
-------------
When you wrap a method with the @LazyDictProperty you create a LazyDictProperty instance,
which is a singleton for the class (i.e. in the example 'Man' has exactly one LazyDictProperty
instance). To enable the _cache (and make method binding a bit neater), each instance of
the class (instances of 'Man' in the example) is given a _LazyDicts attribute which holds a
dict of LazyDict instances built from each of the LazyDictProperties. Note that the _LazyDicts
attribute and its keys/values do not exist until you access the relevant LazyDictProperty.
TODO
-------------------
It would be nice to do something more interesting with the cache, perhaps
have a static method which can look up all instances of the class and clear cache.
...could get a bit complicated to decide what to clear...might need client code to
specify a priority for each thing stored in the cache, where the priority will be applied
globally.
"""
def __init__(self,get_function):
self._get_function = get_function
def _default_iter_function(inst):
raise TypeError("not iterable")
self._iter_function = _default_iter_function
def __get__(self,inst,type=None):
if not hasattr(inst,'_LazyDicts') or inst._LazyDicts is None:
inst._LazyDicts = {}
if self not in inst._LazyDicts:
inst._LazyDicts[self] = LazyDict(partial(self._get_function,inst),partial(self._iter_function,inst))
return inst._LazyDicts[self]
def __set__(self,inst,val):
raise AttributeError
def iter_function(self,iter_function):
self._iter_function = iter_function
return iter_function
class LazyDict():
def __init__(self,get_function,iter_function=None,canClearCache=False):
self._get_function = get_function
self._iter_function = iter_function
self._cache = {} # this is provided for convenience, it doesn't do anything
def __getitem__(self,key):
return self._get_function(key)
def __iter__(self):
return self._iter_function()
def __call__(self,*args,**kwargs):
return self._get_function(*args,**kwargs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment