Search code examples
pythoninheritancepython-3.xdynamiccom

Extend Python object in run-time using inheritance


I would like to extend an object with new attributes and methods, but in run-time. Basically I would prefer to just inherit and extend a class, but new objects of the base class are not usually created using it's constructor but by using fairly complex function.

Instead of...

from win32com import client

excel = client.Dispatch("Excel.Application")
excel.Visible = 1
excel.Workbooks.Add()
print(excel.Range("A1").value)

...I need something like (obviously broken):

from win32com import client

class Excel(client.CDispatch):
    def __init__(self):
        self = client.Dispatch("Excel.Application")

    def get(self, cell):
        return self.Range(cell).value

    def show(self):
        self.Visible = 1

excel = Excel()
excel.show()
excel.Workbooks.Add()  # I want this to be still working
print(excel.get("A1"))

I still would like to be able to use original methods and attributes, but also my new ones. I have trouble wrapping my head around the concept, I am even not sure how to call the principle. Any ideas?

Another way to get desired functionality is like this:

from win32com import client

class Excel():
    def __init__(self):
        self.excel = client.Dispatch("Excel.Application")
        self.Workbooks = self.excel.Workbooks
        # I do not really want to repeat all base class
        # functionality here to bind it to my new class

    def get(self, cell):
        return self.excel.Range(cell).value

    def show(self):
        self.excel.Visible = 1

excel = Excel()
excel.show()
excel.Workbooks.Add()
print(excel.get("A1"))

That works, however requires me to do a lot of lines similar to self.Workbooks = self.excel.Workbooks.


Solution

  • Implementation inheritence is mostly a variant of the composition / delegation pattern. The good news is that Python makes delegation quite easy. I've not tried (not working on Windows) but the following snippet might just work:

    from win32com import client
    
    class Excel(object):
        def __init__(self):
            self._app = client.Dispatch("Excel.Application")
    
        def get(self, cell):
            return self._app.Range(cell).value
    
        def show(self):
            self._app.Visible = 1
    
        def __getattr__(self, name):
            try:
                return getattr(self._app, name)
            except AttributeError:
                raise AttributeError(
                    "'%s' object has no attribute '%s'" % (type(self).__name__, name))