【问题标题】:Decorators on abstract methods抽象方法上的装饰器
【发布时间】:2013-10-20 13:18:51
【问题描述】:

在 python 中,有没有办法让抽象方法上的装饰器传递到派生实现?

例如,在

import abc

class Foo(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    @some_decorator
    def my_method(self, x):
        pass

class SubFoo(Foo):
    def my_method(self, x):
        print x

据我所知,SubFoomy_method 不会被some_decorator 装饰。有没有什么方法可以做到这一点,而不必单独装饰Foo 的每个派生类?

【问题讨论】:

  • 恐怕只有广泛的魔法。调用抽象方法_my_method 并有一个非抽象的、非覆盖的my_method 基本上是some_decorator(self._my_method)(*args, **kwds) 的替代方法吗?
  • @delnan 嗯,是的,我希望在广泛的元类魔法和你的好建议之间有一个解决方案,它有点干净。但我猜我会做我必须做的。

标签: python decorator abc


【解决方案1】:

我会将它编码为两种不同的方法,就像在标准方法工厂模式描述中一样。

https://www.oodesign.com/factory-method-pattern.html

class Foo(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    @some_decorator
    def my_method(self, x):
        self.child_method()

class SubFoo(Foo):
    def child_method(self, x):
        print x

【讨论】:

    【解决方案2】:

    这当然是可能的。用 Python 做不到的东西很少哈哈!好不好由你来决定……

    class MyClass:
        def myfunc():
            raise NotImplemented()
    
        def __getattribute__(self, name):
            if name == "myfunc":
                func = getattr(type(self), "myfunc")
                return mydecorator(func)
            return object.__getattribute__(self, name)
    

    (还没有测试语法,但应该会给你这个想法)

    【讨论】:

      【解决方案3】:

      我的解决方案是扩展超类的方法而不覆盖它。

      import abc
      
      class Foo(object):
          __metaclass__ = abc.ABCMeta
      
          @abc.abstractmethod
          @some_decorator
          def my_method(self, x):
              pass
      
      class SubFoo(Foo):
          def my_method(self, x):
              super().my_method(x)  #delegating the call to the superclass
              print x
      

      【讨论】:

      • 这违背了抽象方法的目的。抽象方法的整个想法是它们必须被子类覆盖。因此,您不要在其上调用super()。事实上,我通常让我的抽象方法引发NotImplementedError,除了被标记为抽象以确保不使用super()
      • 使用抽象方法的默认实现没有任何问题。
      【解决方案4】:

      据我所知,这在 Python 中是不可能的,也不是一个好的策略。这里有更多解释。

      根据abc documentation

      abstractmethod() 与其他方法描述符结合应用时,应作为最内层的装饰器应用,如以下使用示例所示: ...

      换句话说,我们可以这样编写你的类(Python 3 风格):

      from abc import ABCMeta, abstractmethod
      
      class AbstractClass(metclass=ABCMeta):
      
          @property
          @abstactmethod
          def info(self):
              pass
      

      然后呢?如果您从AbstractClass 派生并尝试覆盖info 属性而不指定@property 装饰器,则会造成很大的混乱。请记住,为简洁起见,属性(这只是一个示例)通常对其类方法使用相同的名称:

      class Concrete(AbstractMethod):
      
          @property
          def info(self):
              return
      
          @info.setter
          def info(self, new_info):
              new_info
      

      在这种情况下,如果你不重复 @property@info.setter 装饰器,那会造成混乱。在 Python 术语中,这也不起作用,属性被放置在类本身上,而不是实例上。换句话说,我想这是可以做到的,但在我看来,它最终会产生令人困惑的代码,而且不像重复几行装饰器那样容易阅读。

      【讨论】:

        【解决方案5】:

        Jinksy 的回答对我不起作用,但稍作修改后它确实起作用了(我使用了不同的名称,但想法应该很清楚):

        def my_decorator(func):
            def wrapped(self, x, y):
                print('start')
                result = func(self, x, y)
                print('end')
                return result
            return wrapped
            
        
        
        class A(ABC):
            @abstractmethod
            def f(self, x, y):
                pass
                
            @my_decorator
            def f_decorated(self, x, y):
                return self.f(x, y)
            
        
        class B(A):
            def f(self, x, y):
                return x + y
            
        
        B().f_decorated(1, 3)
        [Out:]
        start
        end
        4   
        

        请注意,这与 Jinksy 所写的重要区别在于抽象方法是 f,而在调用 B().f_decorated 时,它是被调用的继承的非抽象方法。

        据我了解,f_decorated 可以正确定义,因为 abstractmethod 装饰器不会干扰装饰器 my_decorator

        【讨论】:

          猜你喜欢
          • 2016-10-28
          • 1970-01-01
          • 2021-09-17
          • 1970-01-01
          • 2020-09-25
          • 1970-01-01
          • 2019-11-12
          • 2017-05-13
          • 2015-08-28
          相关资源
          最近更新 更多