我想这将是一个有趣的周日早上解决问题。已经3个小时了,我还差得远……
背景:
我dogpile
用来缓存来自SqlAlchemy
.
我将一行数据转换为dict
调用的自定义子类ObjectifiedDict
。这只是让我用对象表示法来处理字典,所以我可以对这些只读字典使用相同的模板和助手,就像我对活动的 SqlAlchemy 对象所做的那样。(我也喜欢将缓存数据存储为字典,因此我可以将相同的缓存值用于其他应用程序或语言)
一切正常。
我一直在探索使缓存更好一点的方法,并记住了 Pyramid 的@reify
装饰器(http://docs.pylonsproject.org/projects/pyramid/en/latest/api/decorator.html)。@reify 让对象方法用函数的返回值覆盖自己,这样底层函数只被调用一次。出于缓存目的,这个想法对我来说非常有吸引力。
[正如评论中提到的,记忆是一个更合适的术语]
问题:
我不知道如何最好地处理这样的事情。尝试动态设置对象属性以重新定义自己比我想象的要困难得多。 @reify
非常简单而且效果很好,因为它在一个预定义的类上。
到目前为止,我想出的最好的就是这个......
class ReifiedObjectFunction(object):
def __init__(self, object , object_attribute, function , *args , **kwargs ):
self.object = object
self.object_attribute = object_attribute
self.function = function
self.args = args
self.kwargs = kwargs
try:
self.__doc__ = function.__doc__
except: # pragma: no cover
pass
def __repr__(self):
val = self.function(*self.args,**self.kwargs)
setattr( self.object , self.object_attribute , val )
return val
class ObjectifiedDict(dict):
def __getattr__(self,attr):
if attr in self:
return self[attr]
return self.__getattribute__(attr)
def lazyload( self, attr , function , *args , **kwargs ):
self[attr] = ReifiedObjectFunction(self,attr,function,*args,**kwargs)
def lazyloaded_function(*args,**kwargs):
print 'compute lazyloaded_function %s,%s' % ( args , kwargs )
return "%s" % args
sample = ObjectifiedDict({'a':'a','b':'bb','c':'ccc'})
print "get a"
print " %s" % sample.a
print "get b"
print " %s" % sample.b
print "set d"
sample.lazyload( 'd' , lazyloaded_function , "dddd" )
print "d is set"
print "sample : %s" % sample
print "get d"
print " %s" % sample.d
print "get d again"
print " %s" % sample.d
print "sample : %s" % sample
不过,这有一些明显且巨大的警告。它仅适用于字符串上下文(因为我正在重载repr)并且我只能返回字符串值。
有人对我应该如何进行有线索吗?
更新:
我想出了这个我想做的工作示例。有没有人看到这种方法有任何问题:
class LazyloadedFunction(object):
def __init__(self, object , object_attribute, function , *args , **kwargs ):
self.object = object
self.object_attribute = object_attribute
self.function = function
self.args = args
self.kwargs = kwargs
try:
self.__doc__ = function.__doc__
except: # pragma: no cover
pass
def execute(self):
val = self.function(*self.args,**self.kwargs)
return val
class ObjectifiedDict(dict):
def __getitem__(self,attr):
if attr in self:
item = dict.__getitem__(self,attr)
if isinstance( item , LazyloadedFunction ):
item = item.execute()
dict.__setitem__( self , attr , item )
return item
def __getattr__(self,attr):
if attr in self:
if isinstance( self[attr] , LazyloadedFunction ):
self[attr] = self[attr].execute()
return self[attr]
return self.__getattribute__(attr)
def __getattribute__(self,attr):
return dict.__getattribute__(self,attr)
def lazyload( self, attr , function , *args , **kwargs ):
self[attr] = LazyloadedFunction(self,attr,function,*args,**kwargs)
def lazyloaded_function(*args,**kwargs):
print 'compute lazyloaded_function %s,%s' % ( args , kwargs )
return [1,2,3]
sample_a = ObjectifiedDict({'a':'a','b':'bb','c':'ccc'})
sample_a.lazyload( 'd' , lazyloaded_function , "dddd" )
sample_b = ObjectifiedDict({'a':'a','b':'bb','c':'ccc'})
sample_b.lazyload( 'd' , lazyloaded_function , "dddd" )
print "sample_a"
print sample_a
print "sample_a.d = %s" % sample_a.d
print sample_a
print "====="
print "sample_b"
print sample_b
print "sample_b['d'] = %s" % sample_b['d']
print sample_b