gpt4 book ai didi

python - 在 python 内存装饰器类中设置 get/set 属性

转载 作者:太空宇宙 更新时间:2023-11-03 11:52:09 26 4
gpt4 key购买 nike

我创建了一个装饰器内存类,我正在积极使用它来缓存我的调用。关于如何实现 python 记忆化,已经有很多很好的建议。

我创建的类当前使用 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)

最佳答案

无法将属性 附加到单个实例。作为描述符,property 必须是类定义的一部分才能发挥作用。这意味着您无法轻松地将它们添加到您在 __get__ 中创建的 partial 对象中。

现在,您可以创建自己的类来使用添加的属性重新实现 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

我建议您将装饰方法的内存相关接口(interface)设置为只能通过类访问,而不是通过实例访问。要使其工作,您需要更新您的 __get__ 方法以在 objNone 时工作。它可以简单地返回 self:

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

通过此更改,通过类在 memo 上使用 property 是可行的:

basic.increment.cacheTimeOut = 20  # set property of the "unbound" method basic.increment

关于python - 在 python 内存装饰器类中设置 get/set 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23770313/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com