Is it possible to dynamically inherit from a class that is only known at runtime in python?
TL;DR: Yes, using python closures
"The class Bar
is somewhat dynamically generated" That's fine... as long as it follows the blueprint (of a class that should be extended by Foo
), you can leverage python closures here. Dynamically create a new class by creating it inside, and returning it from a function.
def get_class(superclass):
class Foo(superclass):
def __init__(self, ...):
...
return Foo
DynamicFoo = get_class(Bar)
myobj = DynamicFoo()
This is a common pattern you'll see in python - leveraging closures to dynamically create callbacks and classes.
The answer above assumes that Bar
is correctly defined, when it in fact is not. The super_cool_function
is missing a self parameter. Instance methods are always called with the first parameter (the instance itself) automatically being passed in as the first attribute.
So, the correct definition for Bar
would be:
class Bar:
def super_cool_function(self):
print("Cool")
Now, defining get_class
with the simplest definition of the inner class Foo
:
def get_class(superclass):
class Foo(superclass):
pass
return Foo
DynamicFoo = get_class(Bar)
myobj = DynamicFoo()
myobj.super_cool_function()
# Cool
Your desired use is a little strange:
foobar = Foo(Bar)
You're constructing a Foo
instance by handing it the Bar
class object, and expecting to get back something that acts like a Bar
instance. Normally, a proxy class is designed to take an object to proxy to, or look on up somewhere, not just construct one with no arguments.
But, other than that oddness, which just means an __init__
method that constructs the object, this is just a bog-standard proxy class. So:
class Foo:
def __init__(self, cls):
self._inst = cls()
def __getattr__(self, name):
return getattr(self._inst, name)
def __setattr__(self, name, value):
if name in {'_inst'}:
super().__setattr__(name, value)
else:
setattr(self._inst, name, value)
def __delattr__(self, name):
delattr(self._inst, name)
Of course you still won't be able to call that super_cool_function
on a foobar
any more than you could on a Bar
instance, because it's defined as a method and doesn't have a self
parameter. But you'll get the same error from the Foo
instance that you would have gotten from a Bar
instance:
>>> foobar.super_cool_function
<bound method Bar.super_cool_function of <__main__.Bar object at 0x129f95080>>
>>> foobar.super_cool_function()
TypeError: super_cool_function() takes 0 positional arguments but 1 was