【问题标题】: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 再次进行包装。我编辑了示例以显示这一点。

标签: python decorator python-decorators class-attributes


【解决方案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?
  • 实例化类包装器
猜你喜欢
  • 1970-01-01
  • 2014-02-08
  • 2018-10-06
  • 2019-08-21
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-14
相关资源
最近更新 更多