Decorated class 无法访问其属性
Decorated class looses acces to its attributes
我实现了一个非常有用的装饰器,直到我向装饰器添加属性 class。当我实例化 class 时,它无法访问 calss 属性。采用以下最小工作示例:
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()
当我实例化包装器 class (#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 变成了一个失去对其属性的访问的函数...
关于如何解决这个问题的任何想法?也许有解决方法
您用 numericalmathfunction
函数对象替换了 Wrapper
(这是一个 class)。该对象没有任何 class 属性,没有。
本质上,装饰器是这样做的:
class Wrapper:
# ...
Wrapper = NumericalMathFunctionDecorator(enableCache=True)(Wrapper)
所以无论 NumericalMathFunctionDecorator.__call__
方法 returns 现在已经取代了 class;所有对 Wrapper
的引用现在都引用 return 值。当您在 __init__
方法中使用名称 Wrapper
时,您引用的是全局变量,而不是原始的 class.
您仍然可以使用 type(self)
访问当前的 class,或者仅通过 self
引用这些属性(名称查找落入 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])
或
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])
在这两种情况下,如果您曾经做过 subclass Wrapper
(无论如何在这种情况下您都不能这样做,那么您最终可能会引用 subclass 上的属性必须从装饰器闭包中取出 class。
或者,您可以将原始 class 作为属性存储在 returned 函数中:
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])
我实现了一个非常有用的装饰器,直到我向装饰器添加属性 class。当我实例化 class 时,它无法访问 calss 属性。采用以下最小工作示例:
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()
当我实例化包装器 class (#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 变成了一个失去对其属性的访问的函数...
关于如何解决这个问题的任何想法?也许有解决方法
您用 numericalmathfunction
函数对象替换了 Wrapper
(这是一个 class)。该对象没有任何 class 属性,没有。
本质上,装饰器是这样做的:
class Wrapper:
# ...
Wrapper = NumericalMathFunctionDecorator(enableCache=True)(Wrapper)
所以无论 NumericalMathFunctionDecorator.__call__
方法 returns 现在已经取代了 class;所有对 Wrapper
的引用现在都引用 return 值。当您在 __init__
方法中使用名称 Wrapper
时,您引用的是全局变量,而不是原始的 class.
您仍然可以使用 type(self)
访问当前的 class,或者仅通过 self
引用这些属性(名称查找落入 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])
或
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])
在这两种情况下,如果您曾经做过 subclass Wrapper
(无论如何在这种情况下您都不能这样做,那么您最终可能会引用 subclass 上的属性必须从装饰器闭包中取出 class。
或者,您可以将原始 class 作为属性存储在 returned 函数中:
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])