在python memoization装饰器类中设置get / set属性
我已经创建了一个装饰器memoization类,我正在积极地用于缓存我的调用.关于如何实现
python memoization已经有很多很好的建议.
我创建的类当前使用get和set方法调用来设置cacheTimeOut.它们被称为getCacheTimeOut()和setCacheTimeOut().虽然这是一个适当的解决方案.我希望使用@property和@ cacheTimeOut.setter装饰器来直接调用函数,例如cacheTimeOut = 120 问题在于细节.我不知道如何在__get__方法中访问这些属性. __get__方法将类中定义的不同函数调用分配给functions.partial. 这是我为Python 2.7设计的脚本示例 import time from functools import partial import cPickle class memoize(object): def __init__(self,func): self.func = func self._cache = {} self._timestamps = {} self._cacheTimeOut = 120 self.objtype = None def __new__(cls,*args,**kwargs): return object.__new__(cls,**kwargs) def __get__(self,obj,objtype=None): """Used for object methods where decorator has been placed before methods.""" self.objtype = objtype fn = partial(self,obj) fn.resetCache = self.resetCache fn.getTimeStamps = self.getTimeStamps fn.getCache = self.getCache fn._timestamps = self._timestamps fn.setCacheTimeOut = self.setCacheTimeOut fn.getCacheTimeOut = self.getCacheTimeOut return fn def __argsToKey(self,**kwargs): args = list(args) for x,arg in enumerate(args): # remove instance from if self.objtype: if isinstance(arg,self.objtype): args.remove(arg) str = cPickle.dumps(args,1)+cPickle.dumps(kwargs,1) return str def __call__(self,**kwargs): """Main calling function of decorator.""" key = self.__argsToKey(*args,**kwargs) now = time.time() # get current time to query for key if self._timestamps.get(key,now) > now: return self._cache[key] else: value = self.func(*args,**kwargs) self._cache[key] = value self._timestamps[key] = now + self._cacheTimeOut return value def __repr__(self): '''Return the function's docstring.''' return self.func.__doc__ def resetCache(self): """Resets the cache. Currently called manually upon request.""" self._cache = {} self._timestamps = {} def getCacheTimeOut(self): """Get the cache time out used to track stale data.""" return self._cacheTimeOut def setCacheTimeOut(self,timeOut): """Set the cache timeout to some other value besides 120. Requires an integer value. If you set timeOut to zero you are ignoring the cache""" self._cacheTimeOut = timeOut def getCache(self): """Returns the cache dictionary.""" return self._cache def getTimeStamps(self): """Returns the encapsulated timestamp dictionary.""" return self._timestamps @property def cacheTimeOut(self): """Get cacheTimeOut.""" return self._cacheTimeOut @cacheTimeOut.setter def cacheTimeOut(self,timeOut): """Set cacheTimeOut.""" self._cacheTimeOut = timeOut memoize def increment(x): increment.count+=1 print("increment.count:%d,x:%d"%(increment.count,x)) x+=1 return x increment.count = 0 # Define the count to track whether calls to increment vs cache class basic(object): def __init__(self): self.count = 0 @memoize def increment(self,x): self.count+=1 print("increment.count:%d,x)) x+=1 return x def main(): print increment(3) print increment(3) # What I am actually doing print increment.getCacheTimeOut() # print out default of 120 increment.setCacheTimeOut(20) # set to 20 print increment.getCacheTimeOut() # verify that is has been set to 120 # What I would like to do and currently does not work print increment.cacheTimeOut # Assign to property increment.cacheTimeOut = 20 myObject = basic() print myObject.increment(3) print myObject.count print myObject.increment(3) print myObject.count print myObject.increment(4) print myObject.count ####### Unittest code. import sys import time import unittest from memoize import memoize class testSampleUsages(unittest.TestCase): # """This series of unit tests is to show the user how to apply memoize calls.""" def testSimpleUsageMemoize(self): @memoize def increment(var=0): var += 1 return var increment(3) increment(3) def testMethodBasedUsage(self): """Add the @memoize before method call.""" class myClass(object): @memoize def increment(self,var=0): var += 1 return var @memoize def decrement(self,var=0): var -=1 return var myObj = myClass() myObj.increment(3) myObj.increment(3) myObj.decrement(6) myObj.decrement(6) def testMultipleInstances(self): @memoize class myClass(object): def __init__(self): self.incrementCountCalls = 0 self.decrementCountCalls = 0 self.powCountCall = 0 # @memoize def increment(self,var=0): var += 1 self.incrementCountCalls+=1 return var # @memoize def decrement(self,var=0): self.decrementCountCalls+=1 var -=1 return var def pow(self,var=0): self.powCountCall+=1 return var*var obj1 = myClass() # Memoizing class above does not seem to work. obj2 = myClass() obj3 = myClass() obj1.increment(3) obj1.increment(3) #obj2.increment(3) #obj2.increment(3) #obj3.increment(3) #obj3.increment(3) obj1.pow(4) obj2.pow(4) obj3.pow(4) 解决方法
无法将属性附加到单个实例.作为描述符,属性必须是类定义的一部分才能运行.这意味着您无法轻松将它们添加到您在__get__中创建的部分对象中.
现在,您可以创建自己的类,以使用添加的属性重新实现partial的行为.但是,我怀疑这种限制实际上对你有利.如果将memo应用于方法,则其状态由类的所有实例共享(甚至可能是子类的实例).如果您允许通过实例调整缓存详细信息,则可能会将用户与以下情况混淆: obj1 = basic() print obj1.increment.getCacheTimeout() # prints the initial value,e.g. 120 obj2 = basic() obj2.increment.setCacheTimeOut(20) # change the timeout value via another instance print obj1.increment.getCacheTimeout() # the value via the first instance now prints 20 我建议您只允许通过类访问装饰方法的与memoization相关的接口,而不是通过实例.要使其工作,如果obj为None,则需要更新__get__方法.它可以简单地回归自我: def __get__(self,objtype=None): if obj is None: return self self.objtype = objtype return partial(self,obj) # no need to attach our methods to the partial anymore 通过此更改,通过类使用备忘录上的属性: basic.increment.cacheTimeOut = 20 # set property of the "unbound" method basic.increment (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |