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.
source to share
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 :)
source to share
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
source to share