装饰类无法访问其属性
Posted
技术标签:
【中文标题】装饰类无法访问其属性【英文标题】:Decorated class looses acces to its attributes 【发布时间】:2015-08-23 01:43:51 【问题描述】:在我将属性添加到装饰类之前,我实现了一个像魅力一样工作的装饰器。当我实例化该类时,它无法访问 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()
当我实例化 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 变成了一个失去对其属性的访问的函数...
关于如何解决这个问题的任何想法?也许有一个解决方法
【问题讨论】:
为什么你的类装饰器返回一个函数? 包装器是一个函数 @MartijnPieters,Wrapper 是一个可调用的类,稍后需要通过specialfunction
再次进行包装。我编辑了示例以显示这一点。
【参考方案1】:
您将 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
,则最终可能会引用子类上的属性(在这种情况下无论如何您都不能这样做,因为您必须将类从装饰器闭包中取出)。
或者,您可以将原始类存储为返回函数的属性:
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])
【讨论】:
使用 self.places 解决了这个问题。然而,我刚刚碰到了另一个。在__init__
方法中,我还调用了基类的__init__
:super(Wrapper, self).__init__(4, 1)
,它会抛出TypeError: must be type, not function
。我会尝试你的最后一个解决方案,看看它能做什么
@FelipeAguirre:同样,Wrapper
是全局名称。在那里使用type(self)
实际上是非常错误的,因为在子类化时会中断,最好的选择是在这种情况下使用__wrapped__
属性选项。
好的,我试过了,但它返回一个NoneType
。 (只是你定义的__init__
,没有添加super(cls.__wrapped__).__init__(4, 1)
)
@FelipeAguirre:什么返回None
?
实例化类包装器以上是关于装饰类无法访问其属性的主要内容,如果未能解决你的问题,请参考以下文章