1

我想这将是一个有趣的周日早上解决问题。已经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
4

0 回答 0