我实现了一个像魅力一样工作的装饰器,直到我给装饰类添加属性。当我实例化类时,它不能访问类属性。以以下最小的工作示例为例:
from module import specialfunction
class NumericalMathFunctionDecorator:
def __init__(self, enableCache=True):
self.enableCache = enableCache
def __call__(self, wrapper):
def numericalmathfunction(*args, **kwargs):
func = specialfunction(wrapper(*args, **kwargs))
"""
Do some setup to func with decorator arguments (e.g. enableCache)
"""
return numericalmathfunction
@NumericalMathFunctionDecorator(enableCache=True)
class Wrapper:
places = ['home', 'office']
configs = {
'home':
{
'attr1': 'path/at/home',
'attr2': 'jhdlt'
},
'office':
{
'attr1': 'path/at/office',
'attr2': 'sfgqs'
}
}
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
assert where in Wrapper.places, "Only valid places are {}".format(Wrapper.places)
self.__dict__.update(Wrapper.configs[where])
def __call__(self, X):
"""Do stuff with X and return the result
"""
return X ** 2
model = Wrapper()
当我实例化Wrapper类(#1)时,我得到以下错误:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-a99bd3d544a3> in <module>()
15 assert where in Wrapper.places, "Only valid places are {}".format(Wrapper.places)
16
---> 17 model = Wrapper()
<ipython-input-5-a99bd3d544a3> in numericalmathfunction(*args, **kwargs)
5 def __call__(self, wrapper):
6 def numericalmathfunction(*args, **kwargs):
----> 7 func = wrapper(*args, **kwargs)
8 return numericalmathfunction
9
<ipython-input-5-a99bd3d544a3> in __init__(self, where)
13 def __init__(self, where='home'):
14 # Look for setup configuration on 'Wrapper.configs[where]'.
---> 15 assert where in Wrapper.places, "Only valid places are {}".format(Wrapper.places)
16
17 model = Wrapper()
AttributeError: 'function' object has no attribute 'places'
我猜有了这个装饰器,Wrapper就变成了一个失去对其属性访问的函数…
有什么办法可以解决这个问题吗?也许有一个解决方法
您将Wrapper
(这是一个类)替换为numericalmathfunction
函数对象。该对象没有任何类属性,没有
class Wrapper:
# ...
Wrapper = NumericalMathFunctionDecorator(enableCache=True)(Wrapper)
所以无论NumericalMathFunctionDecorator.__call__
方法返回什么,现在已经取代了类;所有对Wrapper
的引用现在都引用该返回值。当您在__init__
方法中使用名称Wrapper
时,您引用的是该全局变量,而不是原始类。
您仍然可以使用type(self)
访问当前类,或者仅通过self
引用这些属性(其中名称查找将通过类):
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
assert where in self.places, "Only valid places are {}".format(self.places)
self.__dict__.update(self.configs[where])
或
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
cls = type(self)
assert where in cls.places, "Only valid places are {}".format(cls.places)
self.__dict__.update(cls.configs[where])
在这两种情况下,如果你曾经做过子类Wrapper
,你最终都可以引用子类上的属性(在这种情况下你无论如何都不能这样做,因为你必须从decorator闭包中钓出类)。
或者,您可以将原始类存储为返回函数的属性:
def __call__(self, wrapper):
def numericalmathfunction(*args, **kwargs):
func = specialfunction(wrapper(*args, **kwargs))
"""
Do some setup to func with decorator arguments (e.g. enableCache)
"""
numericalmathfunction.__wrapped__ = wrapper
return numericalmathfunction
然后在你的__init__
:
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
cls = Wrapper
while hasattr(cls, '__wrapped__'):
# remove any decorator layers to get to the original
cls = cls.__wrapped__
assert where in cls.places, "Only valid places are {}".format(cls.places)
self.__dict__.update(cls.configs[where])