Extend an instance at runtime

This question has been rejected on StackExchange and I haven't found an answer on StackOverflow yet.

Note: the code is working and I am asking for ideas, best options and opinions (performance, reliability) for extending class instances at runtime.

For example, let's say my original class is Foo and I want to "extend" it with Bar.

The specific limitations I currently have are:

  • Python 2 is compatible even if I'm curious about Python 3, if any
  • I am getting an object from another library, I would like to add functionality (note: this is an "old style class")
  • I need him to leave it by his parents, so isststance (like Foo) keep working
  • It doesn't have to rerun init from the first class (there are stateful connections)
  • It would be nice to check its inheritance in the "new" class (for example: isinstance (instance, Bar))

I wonder if there is something with the new , metaclass or mixes. But I have not achieved anything with them yet.

What i tried

Use as a base:

class Foo:
    def foo(self): return 'foo'

class Bar:
    def bar(self): return self.foo() + 'bar'

foo = Foo()

      

create a new object related to the previous one

It works, but it creates a new object, and I wanted to do without that (and I think the test is a useless utility test):

class Bar(Foo, object):
    def __init__(self, foo):
        self.__foo = foo
    def __getattr__(self, name):
        try:
            return getattr(self.__foo, name)
        except:
            raise AttributeError
    def bar(self):
        return self.foo() + 'bar'

bar.foo() # OK
bar.bar() # OK
isinstance(bar, Foo) # OK
isinstance(bar, Bar) # OK

      

instance dictionary update

Almost works, but Bar doesn't appear in inheritance:

foo.__class__.__dict__.update(Bar.__dict__)
foo.bar()            # does work
isinstance(foo, Foo) # True
isinstance(foo, Bar) # False :(

      

using .MethodType types

Almost, but Bar doesn't show up in inheritance, and I have to check if the attribute I'm fixing is callable or not:

for k, v in Bar.__dict__.iteritems():
    if callable(v):
        setattr(foo, k, types.MethodType(v, foo))

foo.foo() # OK
foo.bar() # OK
isinstance(foo, Foo) # OK
isinstance(foo, Bar) # False

      

Please enlighten me.

+3


source to share


2 answers


CAVEAT: This is not pure programming! But did you know that :-)

The easiest way is to have Bar subclass Foo and then dynamically change the type of your instance:

class Foo:
    def foo(self): return 'foo'

class Bar(Foo):
    def bar(self): return self.foo() + 'bar'

foo = Foo()

foo.__class__ = Bar

print foo.bar()

      



In response to the question in the comment:

>>> class Foo:
...     def foo(self): return 'foo'
... 
>>> class Bar(Foo):
...     def bar(self): return self.foo() + 'bar'
... 
>>> foo = Foo()
>>> 
>>> foo.__class__ = Bar
>>> 
>>> print foo.bar()
foobar
>>> isinstance(foo, Bar)
True
>>> isinstance(foo, Foo)
True

      

In response to another comment: yes it really is that simple :)

+3


source


If you want to work, you can add Bar to Foo.__bases__

:



class Foo:
    def foo(self): return 'foo'

class Bar:
    def bar(self): return self.foo() + 'bar'

foo = Foo()
Foo.__bases__ = Foo.__bases__ + (Bar,)

print(foo.bar())
print isinstance(foo, Foo) 
print(isinstance(foo, Bar))
foobar
True
True

      

+2


source







All Articles