Last active
August 29, 2015 13:58
-
-
Save d1manson/10409675 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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