python 类装饰器 __set___在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,*args, **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, *args, **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, *args, **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:%d"%(increment.count, 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, obj, 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

标签:python,memoization,python-decorators

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值