Search code examples
pythondynamicoverridinggetattrgetattribute

Dynamic override methods of Python base class


I am given a Python base class that defines an interface which I must implement in a derived class. In the base class, each method is defined as a stub that throws NotImplementedError.

There are many such methods and their names are mostly very regular, of the form <Operation><Type>. In principle the vast majority could be implemented by dispatching to a single method that takes "<Operation>" and "<Type>", parsed from the method name, as arguments. I would like to do something like this to avoid boilerplate. However, there are a small number of methods that would need an explicit override.

class Base(object):
    ...
    # 'Regular' methods - the majority of the interface
    def CreateObj1(self, arg):
        raise NotImplementedError()
    def DeleteObj1(self, arg):
        raise NotImplementedError()
    # etc.. for a number of such operations
    #       for many type Obj1, Obj2, ...

    # A few methods that don't fit the above pattern
    def SpecialMethod1(self, arg):
        raise NotImplementedError()
    ...

class Derived(Base):
    ...
    def _impl(self, operation_name, object_type, arg):
        # With this I am essentially able to implement
        # all of the 'regular' methods
        ...

    def CreateObj1(self, arg):
        return self._impl('Create', 'Obj1', arg)
    def DeleteObj1(self, arg):
        return self._impl('Delete', 'Obj1', arg)

    def SpecialMethod1(self, arg):
        # This still needs an explicit implementation
        ...
    ... 

I could just implement it as above but I would prefer not to have to write all of the explicit delegators for the 'regular' methods as it adds a place that needs maintenance when the interface is extended.

My first thought was to define a __getattr__ method, but the problem is that the base stub methods 'win' and __getattr__ is not called. I think I might be able to make it work with __getattribute__ but I have never needed to use this before and am wary because I understand that one can easily get into a mess by misusing it.

Does __getattribute__ sound like the correct way to go with this or are there other approaches that I might be missing?

If it is the right approach, are there any particular patterns I could follow to ensure that I use it correctly?

What would be the best way to deal with the small number of methods that cannot be dynamically overridden and which need explicit implementations in the derived class?


Solution

  • You can use a metaclass (handle with care)

    class BaseMeta(type):
        def get_default_impl(base_attr, subject="Obj1"):
            # modify this method
            action, _ = base_attr.split(subject)
    
            def _default(self, arg):
                return self._impl(action, subject, arg)
            return _default
    
        def __new__(mcs, name, bases, attrs):
            # print("attrs:", attrs, "base:", bases)
            for base in bases:
                for base_attr in dir(base):
                    if base_attr.endswith("Obj1"):  # modify this
                        attrs.setdefault(base_attr, mcs.get_default_impl(base_attr))
    
            def _impl(self, operation_name, object_type, arg):
                raise NotImplementedError()
    
            attrs.setdefault("_impl", _impl)
            return super().__new__(mcs, name, bases, attrs)
    
    
    class Derived(Base, metaclass=BaseMeta):
        def _impl(self, operation_name, object_type, arg):
            print("got op:{}, obj: {}, arg: {}".format(operation_name, object_type, arg))
    

    You will need to modify if base_attr.endswith("Obj1"): and get_default_impl according to how the methods on the base class are defined. I've worked with the example you've posted.