I’ve got a piece of third party software containing classes that I want to modify by subclassing. My problem is that one of the third party classes is not accessed directly but returned by the third party class through a method call.


factory = ThirdParty.Factory() 
node = factory.getNode() #node is an instance of class ThirdParty.Node

I however want the Factory to return my subclassed version of node

nodeSubclass = factory.getNode()

Now I'm thinking can make a subclass of Factory and can override its getNode() method like so:

factorySubclass = MyFactorySubclass()
nodeSubclass = factorySubclass.getNode()

but I am not sure how I convert the instance of node returned within that to an instance of my NodeSubclass with all its attributes but also the methods I override and add.

I'm not even sure its possible to turn an instance into a subclass of that instance?


I'm not sure that's possible either. You can take the factory class and replace its getNode method with your own custom function:

def myFunc(self):
    pass #This is where you do something.
Factory.getNode = myFunc

I usually try to avoid these kinds of messy solutions, but I can't think of anything else.

Thanks scru, but I think I'm in the same boat. Your myFunc() is similar to subclassing ThirdParty.Factory and overriding the getNode() method.

I'm still stuck with the basic dilemma of how to convert the Node object node to a subclassed instance with the same state but overridden and added methods.

This code illustrates the problem more clearly:

class Factory(object):
    def __init__(self, *args)
    def getNode(self, *args):
        newargs = self._getValues(*args)
        node = Node(*args)
        node = self._doSomethingToChangeState(node, newargs)
        return node

class FactorySubclass(ThirdParty.Factory)
    def __init__(self, *args):
        ThirdParty.Factory.__init__(self, *args)
    def getNode(self, *args):
        node = ThirdParty.Factory.getNode(self, *args)
        nodeSubclass = convertToSubclass(node)
        return nodeSubclass

The problem is caused by the fact that the factory can do whatever it wants to the initialized Node object as represented by the _doSomethingToChangeState and _getValues calls. I have no idea what is being set there nor should I really have to from an object oriented perspective. My dilemma is what can be done, and I represent it as a function convertToSubclass call although it could be some built in feature of the python language, to take that node object with its current state and features and make it an instance of my NodeSubclass with my method extensions and overrides.

I don't have the right lingo to really search for a solution but it seems like this could be a universal problem in the Object Oriented world where you get instances of classes (objects) handed to you from some other objects method call (factory) or even a plain old function. These methods\functions can do any unlimited number of things to object you are interested in subclassing to set up its state and attributes before handing it back to you.

Ah I see what you mean now. I'm not exactly sure how you would go about doing this. I'm not certain if there is a python function that can promote an object from a parent type to a subtype.

You can always however attach methods an attributes to objects at whim, allowing you to achieve the same effect (or close) of subtyping. If having a subclass of node is not as important as just adding your own methods and attributes, you can add them on your own in place of the convertToSubclass function above.

If you must subclass, there's something else you might want to try:
create the subclass as your normally would, and write a function that takes a ThirdParty.node object and copies it's attributes unto your own subclass instance. This might just work assuming you copy all of the attributes (including the private ones; for this you might need to look at the source code for ThirdParty.node)

By the way, when you say you don't access the type directly, do you mean that type isn't inside the module, or that you just can't create new instances of it in the conventional way? If the former is true, then the second proposed solution is moot.

If you must subclass, there's something else you might want to try:
create the subclass as your normally would, and write a function that takes a ThirdParty.node object and copies it's attributes unto your own subclass instance. This might just work assuming you copy all of the attributes (including the private ones; for this you might need to look at the source code for ThirdParty.node)

This appears to be the best solution. I'm pointing the original Node object's __dict__ to my subclass __dict__. This removes the need to read over the source code (whew!).

So mocking up some simple sample code:

class Node(object):
    def __init__(self, arg):
        self.arg = arg
        self._private = '123'
    def printAttr(self):
        print '%s & %s' % (self.arg, self._private)

class NodeSubclass(Node):
    def __init__(self, node):
        self.__dict__ = node.__dict__
        self.node = node
    def printAttr(self, arg):
        print '%s &' % arg , 
    def setNew(self, arg):
        self.arg = arg
class Factory(object):
    def returnNode(self, arg):
        return Node(arg)

class FactorySubclass(Factory):
    def returnNode(self, arg):
        node = Factory.returnNode(self, arg)
        return NodeSubclass(node)

factorySubclass = FactorySubclass()
nodeSubclass = factorySubclass.returnNode('abc')

outputs as this:

xyz & abc & 123
789 & 123

To answer you last post. I can access all third party types directly, but I still have to worry that the factory class involved is performing hidden operations. I can see the code, but I prefer to operate in the spirit of OO and assume that only the public interface is visible as I don't want to keep track of additions and deletions of private members that the third party author may make in the future.

I will test this with the actual third party software when I get some more time. The sample code above works, but I feel its hackish. That something from the Node object is not going to be picked up possibly. Something like this also seems limited too python which allows easy access to all the objects attributes\fields via __dict__. I know this is about python, but it seems like OO would have evolved a mechanism that python would have implemented to solve this factory problem where the process of instantiating the objects you want to subclass are handled by the third party code. Especially considering that languages like Java, for instance, have true private members that could not be easily accessed via a simple __dict__ copy containing all public\pseudo-private attributes.

So this will work, I just fear that I'm missing some built-in feature that was designed to handle problems like this flawlessly. I'll give this thread a little more time to see if someone may know what this built-in feature may be or confirm that it doesn't exist before marking as solved.

Thanks for your help scru.

I made a small experiment:

>>> class A(object):
...   def __init__(self):
...     self.value = 12
>>> a = A()
>>> class B(A):
...   def printvalue(self):
...     print self.value
>>> a.__class__ = B
>>> a.printvalue()

so the object 'a' was turned into a 'B'. What do you think of this ? black magic ?

commented: oooh aaaaah +2
commented: pretty cool +5

Another possibility is to add methods to the Thirdparty.Node class instead of writing a subclass. You can do this using a mixin class. Here is a possible code

class Node(object):
    def foo(self):
        foo.x = 123

class MoreMethods(object):
    def another(self):
        self.y = self.x
        print self.x

from ljmixin import MixIn
MixIn(Node, MoreMethods) # add methods to class Node

Here I'm using a MixIn function which I found in an old article, but it still works

# ljmixin.py copied from http://www.linuxjournal.com/node/4540/print
# offers a MixIn function to dynamically mix classes a run time.

import types
def MixIn(pyClass, mixInClass, makeAncestor=0):
   if makeAncestor:
     if mixInClass not in pyClass.__bases__:
        pyClass.__bases__ = (mixInClass,) + pyClass.__bases__
     # Recursively traverse the mix-in ancestor
     # classes in order to support inheritance
     baseClasses = list(mixInClass.__bases__)
     for baseClass in baseClasses:
        MixIn(pyClass, baseClass)
     # Install the mix-in methods into the class
     for name in dir(mixInClass):
        if not name.startswith('__'):
        # skip private members
           member = getattr(mixInClass, name)
           if type(member) is types.MethodType:
               member = member.im_func
           setattr(pyClass, name, member)

Thanks Gribouillis.

I like the a.__class__ = B . It does seem like black magic but so much of python seems like magic anyway. I am curious to know if you can give a general explanation for why this works?

I can explain my line 10 above that states self.__dict__ = node.__dict__ by saying that I am simply getting a reference or pointer to the base classes __dict__ and thus all its attributes and current state. My big concern with that solution is that I may be missing something about the state of the of the base class instance.

I think I will keep away from the MixIn. Lots of extra code and complexity added there. Seems like there could be name clashes also if the classes have method with the same name. So I'm not sure how those are resolved while preserving the ability to call the base class method that you are overriding from the method of the same name as I do in line 25 above.


I'm not exactly clear what your link on polymorphism is in response to on this thread. Can you clarify?

I think paulthom meant to explain why the a.__class__ = B trick works. I think the main reason is that all the OO properties of an object in python rely on the way the object is accessing it's attributes (in python, accessing a method is not different from accessing an ordinary member). To access an attribute, python first looks il the object's dict, and if the attribute is not found, it looks in the class which is accessed through the __class__ pointer of the object.
So if a.__class__ is B, 'a' is a 'B' from python's point of vue.
I think this wouldn't work easily with built-in classes and classes with a __slot__ attribute.