我试图在类中的某些方法上实现一个装饰器,这样如果尚未计算该值,该方法将计算该值,否则它将只返回预先计算的值,该值存储在实例defaultdict
。我似乎无法弄清楚如何从类外部声明的装饰器内部访问实例defaultdict
。关于如何实现这个的任何想法?
以下是导入(作为工作示例):
from collections import defaultdict
from math import sqrt
这是我的装饰者:
class CalcOrPass:
def __init__(self, func):
self.f = func
#if the value is already in the instance dict from SimpleData,
#don't recalculate the values, instead return the value from the dict
def __call__(self, *args, **kwargs):
# can't figure out how to access/pass dict_from_SimpleData to here :(
res = dict_from_SimpleData[self.f.__name__]
if not res:
res = self.f(*args, **kwargs)
dict_from_SimpleData[self.f__name__] = res
return res
这里是带有修饰方法的SimpleData类:
class SimpleData:
def __init__(self, data):
self.data = data
self.stats = defaultdict() #here's the dict I'm trying to access
@CalcOrPass
def mean(self):
return sum(self.data)/float(len(self.data))
@CalcOrPass
def se(self):
return [i - self.mean() for i in self.data]
@CalcOrPass
def variance(self):
return sum(i**2 for i in self.se()) / float(len(self.data) - 1)
@CalcOrPass
def stdev(self):
return sqrt(self.variance())
到目前为止,我已经尝试在SimpleData中声明装饰器,尝试使用装饰器传递多个参数(显然你不能这样做),然后在我试图将纸飞机扔进我的蝎子坦克时转向我的转椅。任何帮助将不胜感激!
答案 0 :(得分:4)
定义装饰器的方式会丢失目标对象信息。请改用函数包装器:
def CalcOrPass(func):
@wraps(func)
def result(self, *args, **kwargs):
res = self.stats[func.__name__]
if not res:
res = func(self, *args, **kwargs)
self.stats[func.__name__] = res
return res
return result
wraps
来自functools
,此处并非绝对必要,但非常方便。
旁注:defaultdict
采用工厂函数参数:
defaultdict(lambda: None)
但是,既然您正在测试密钥是否存在,那么您应该更喜欢简单的dict
。
答案 1 :(得分:2)
定义函数时,您无法执行所需操作,因为它未绑定。这是在运行时以通用方式实现它的一种方法:
class CalcOrPass(object):
def __init__(self, func):
self.f = func
def __get__(self, obj, type=None): # Cheat.
return self.__class__(self.f.__get__(obj, type))
#if the value is already in the instance dict from SimpleData,
#don't recalculate the values, instead return the value from the dict
def __call__(self, *args, **kwargs):
# I'll concede that this doesn't look very pretty.
# TODO handle KeyError here
res = self.f.__self__.stats[self.f.__name__]
if not res:
res = self.f(*args, **kwargs)
self.f.__self__.stats[self.f__name__] = res
return res
一个简短的解释:
__get__
(因此被称为描述符)。而属性访问的默认行为是从对象的字典中获取它,如果定义了描述符方法,Python将调用它。object.__getattribute__
将b.x
之类的访问权转换为type(b).__dict__['x'].__get__(b, type(b))