Home > Software design >  Dynamically implement method by composition
Dynamically implement method by composition

Time:12-20

I am using Python, and I have an interface (a class that only defines the methods signature), that is implemented by two other classes. Now I am adding another wrapper class that implement the interface/base-class, but is actually getting one of the other classes and this wrapper class in most cases just calls one of the two classes. I would like to do some Python magic to avoid defining all methods only the one that I really change.

Here is an example:

class SomeInterface(object):
    def do_1(self):
        raise Exception("please implement")

    def do_2(self):
        raise Exception("please implement")

    def do_3(self):
        raise Exception("please implement")


class Goo(SomeInterface):
    def do_1(self):
        print("I am goo 1")

    def do_2(self):
        print("I am goo 2")

    def do_3(self):
        print("I am goo 3")

class Foo(SomeInterface):
    def do_1(self):
        print("I am foo 1")

    def do_2(self):
        print("I am foo 2")

    def do_3(self):
        print("I am foo 3")


class WrappingInterface(SomeInterface):
    def __init__(self, inf: SomeInterface):
        self._inner = inf

    def do_1(self):
        self._inner.do_1()

    def do_2(self):
        self._inner.do_2()

    def do_3(self):
        self._inner.do_2()
        print("And then doing something else")

Basically, instead of writing do_1 and do_2 in the wrapper class, I would like to do some magic and be able to only write the relevant code.

I tried to implement it using Mixin, but it did not work. Maybe decorator can do it, but I am not sure how.

CodePudding user response:

I don't really understand why do you want to do it like that way. You can inherit your WrappingInterface from the child class (Eg.: from Foo):

Code:

class WrappingInterface(Foo):

    def do_3(self):
        super().do_3()
        print("And then doing something else")


test = WrappingInterface()
test.do_1()
test.do_2()
test.do_3()

Output:

>>> python3 test.py 
I am foo 1
I am foo 2
I am foo 3
And then doing something else

But if you really needed to extend the base classes then you can do it like this:

Dynamically extend base classes:

class WrappingInterface(SomeInterface):
    def __init__(self, inf: SomeInterface):
        self._inner = inf

    def do_3(self):
        self._inner.do_3()
        print("And then doing something else")


WrappingInterface = type("WrappingInterface", (Foo,), dict(WrappingInterface.__dict__))
test = WrappingInterface(Foo())
test.do_1()
test.do_2()
test.do_3()

Output:

> python3 test.py 
I am foo 1
I am foo 2
I am foo 3
And then doing something else

Or you can do everything inside your class (It's a little hacky but it works).

Code:

class WrappingInterface(SomeInterface):
    def __init__(self, inf: SomeInterface):
        self._inner = inf()
        methods = [method for method in dir(inf) if not method.startswith('__')
                   and callable(getattr(inf, method))]
        for method in methods:
            if method in self.__class__.__dict__:
                continue
            setattr(WrappingInterface, method, getattr(inf, method))

    def do_3(self):
        self._inner.do_3()
        print("And then doing something else")


test = WrappingInterface(Foo)
test.do_1()
test.do_2()
test.do_3()

Output:

>>> python3 test.py 
I am foo 1
I am foo 2
I am foo 3
And then doing something else

Note:

I recommend to check the Abstract Base Classes in Python to do a "real" Abstraction. Reference: https://docs.python.org/3/library/abc.html

  • Related