Search code examples
pythonoopinheritanceoverridingdecorator

Python class decorator that overrides internal method


I'm trying to write a decorator that can be used to override a method during inheritance, but externally. Something like:

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'

If anyone could share some ideas on this, that would be great!


Solution

  • Here is how I would go about solving the issue based on use-case;

    1. FOR TESTS - I would look into the mock library if you are trying to dynamically override methods. Here is a resource - https://docs.python.org/3/library/unittest.mock.html

    2. FOR SEPARATE CLASS - If it is class-wide change I would go with a child class that overrides the original method; this will make your code much more explicit

      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. FOR SPECIFIC SITUATION - If you only want this behavior to occur sometimes, I would use the state-observer code pattern - here is an example

      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'