【问题标题】:Python class decorator that overrides internal method覆盖内部方法的 Python 类装饰器
【发布时间】:2021-10-04 15:42:11
【问题描述】:

我正在尝试编写一个装饰器,可用于在继承期间覆盖方法,但在外部。 比如:

class A:
   def some_method(self):
       print('does stuff')

def override(method):
    # override decorator
    ....

def some_method_override():
    print('does other stuff')

@override(method, some_method_override)
class B:
    #overridden

a = A()
b = B()
a.some_method() # prints 'does stuff'
b.some_method() # prints 'does other stuff'

如果有人能就此分享一些想法,那就太好了!

【问题讨论】:

  • 为什么?这似乎是一件很奇怪的事情,没有真正的好处和一些奇怪的警告(比如打破单参数super)。
  • @user2357112supportsMonica 可能有许多类继承自 A 并以相同的方式覆盖特定方法,但会更改其他方法 - 这样您就可以重用方法
  • 如果您尝试动态覆盖方法,我会查看模拟库。这是一个资源 - docs.python.org/3/library/unittest.mock.html
  • (哎呀,这意味着在我之前的评论中打破了 0-argument super - 单参数 super 是一个奇怪的历史文物,几乎对任何事情都没有用处)

标签: python oop inheritance overriding decorator


【解决方案1】:

以下是我将如何根据用例解决问题;

  1. 用于测试 - 如果您尝试动态覆盖方法,我会查看模拟库。这是一个资源 - https://docs.python.org/3/library/unittest.mock.html

  2. 对于单独的类 - 如果是类范围的更改,我会选择一个覆盖原始方法的子类;这将使您的代码更加明确

    class A:
        def some_method(self):
            print('do_stuff_a')
    
    class B(A):
        def some_method(self):
            print('do_stuff_b')
    
    a = A()
    a.some_method() # print 'do_stuff_a'
    b = B()
    a.some_method() # prints 'do_stuff_b'
    
  3. 针对特定情况 - 如果您只想偶尔发生这种行为,我会使用状态观察者代码模式 - 这是一个示例

    class A:
        def __init__(self)
            self._use_method_a = True
    
        def use_method_a(self, use_method_a):
            self._use_method_a = use_method_a
    
        def some_method(self):
            if self._use_method_a:
                return self._method_a()
            else:
                return self._method_b()
    
        def _method_a(self):
            print('do_stuff_a')
    
        def _method_b(self):
            print('do_other_stuff_b')
    
    a = A()
    a.some_method() # print 'do_stuff_a'
    a.use_method_a(False) 
    a.some_method() # prints 'do_other_stuff_b'
    

【讨论】:

    【解决方案2】:

    正如 cmets 中的其他人所提到的,我认为这里不需要装饰器。这只是一个类属性分配的问题。

    定义类时不会发生继承;来自父类的方法不会“拉入”到子类中。相反,它发生在属性查找期间。如果未定义 B.some_method,则使用 A.some_method 的值。因此,要覆盖父类方法,您只需提供所需类属性的定义。方法只是具有function 类型值的类属性。

    class A:
       def some_method(self):
           print('does stuff')
    
    
    def some_method_override(self):
        print('does other stuff')
    
    
    class B(A):
        some_method = some_method_override
    
    a = A()
    b = B()
    a.some_method() # prints 'does stuff'
    b.some_method() # prints 'does other stuff'
    

    如果你确实想要一个装饰器,它只是类似于

    def override(old: str, new: Callable):
        def _(cls):
            setattr(cls, old, new)
            return cls
        return _
    
    
    def some_method_override(self):
        print('does other stuff')
    
    
    class A:
       def some_method(self):
           print('does stuff')
    
    
    @override('some_method', some_method_override)
    class B(A):
        pass
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-28
      • 2017-01-31
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 2016-09-02
      • 2013-01-19
      • 1970-01-01
      相关资源
      最近更新 更多