问题
I implemented a decorator that worked like a charm until I added attributes to the decorated class. When I instantiate the class, it cannot acces the calss attributes. Take the following minimal working example :
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()
When I instantiate the Wrapper class (#1), I get the following error :
---------------------------------------------------------------------------
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'
I guess that with the decorator, Wrapper becomes a function that looses acces to its attributes...
Any ideas of how I can solve this ? Maybe there is a workaround
回答1:
You replaced Wrapper
(which was a class) with the numericalmathfunction
function object. That object doesn't have any of the class attributes, no.
In essence, the decorator does this:
class Wrapper:
# ...
Wrapper = NumericalMathFunctionDecorator(enableCache=True)(Wrapper)
so whatever the NumericalMathFunctionDecorator.__call__
method returns has now replaced the class; all references to Wrapper
now reference that return value. And when you use the name Wrapper
in the __init__
method, you are referencing that global, not the original class.
You can still access the current class with type(self)
, or just reference those attributes via self
(where the name lookup falls through to the class):
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])
or
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])
In both cases you can end up with referencing an attribute on a subclass if you ever did subclass Wrapper
(which you cannot do in this case anyway as you would have to fish the class out of the decorator closure).
Alternatively, you could store the original class as an attribute on the returned function:
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
then use that reference in your __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])
来源:https://stackoverflow.com/questions/30711730/decorated-class-looses-acces-to-its-attributes