I want to choose from which class I inherit at runtime, either class A or B, depending on an argument to my init function in AorB. I already tried the following code, but the methods are not overloaded the way I want them to be overloaded: AorB("B").a() returns A.a() instead of B.a(). How do I choose from which class I inherit at runtime?
Update: From the reaction below I tried the following code. Now I want to inherit AorB in class C, which doesn't work yet:
class A(object):
def a(self):
return "I'm A.a"
def b(self):
return "I'm A.b"
class B(object):
def a(self):
return "I'm B.a"
def c(self):
return "I'm B.c"
def AorB(classname, cache={}):
if not classname in cache:
Base = globals()[classname]
class AorB(Base):
def __init__(self):
print(classname)
Base.__init__(self)
cache[classname] = AorB
return cache[classname]()
class C(AorB):
def __init__(self, classname):
AorB.__init__(classname)
if __name__ == "__main__":
a = AorB("A")
print("A.a:", a.a())
print("A.b:", a.b())
b = AorB("B")
print("B.a:", b.a())
print("B.c:", b.c())
c = C("B")
print("C.a:", c.a())
print("C.c:", c.c())
yields
Traceback (most recent call last):
File "classtest.py", line 28, in <module>
class C(AorB):
TypeError: Error when calling the metaclass bases
function() argument 1 must be code, not str
instead of:
A
('A.a:', "I'm A.a")
('A.b:', "I'm A.b")
B
('B.a:', "I'm B.a")
('B.c:', "I'm B.c")
B
('C.a:', "I'm B.a")
('C.c:', "I'm B.c")
-
Why would you want dynamic inheritance?Jon Clements– Jon Clements2013年03月05日 13:36:35 +00:00Commented Mar 5, 2013 at 13:36
-
Why not have them both and then decide at run-time? or remove the reason for inheritance and have all functions/methods have A/B sides?Inbar Rose– Inbar Rose2013年03月05日 14:15:41 +00:00Commented Mar 5, 2013 at 14:15
-
I want to make connection to a device, which is possible via different connection types. The commands send to the device are identical for all connection types, however, the write and read functions differ (also some other functions). I would like to create a device class like: calculator = Calculator("IP","10.0.0.1"), or calculator = Calculator("USB","/dev/ttyUSB0")Vasco– Vasco2013年03月05日 15:19:58 +00:00Commented Mar 5, 2013 at 15:19
3 Answers 3
class A(object):
def a(self):
return "I'm A.a"
def b(self):
return "I'm A.b"
class B(object):
def a(self):
return "I'm B.a"
def c(self):
return "I'm B.c"
def make_AorB(Base, classname):
class AorB(Base):
def __init__(self):
print(classname)
Base.__init__(self)
return AorB
def make_C(Base, classname):
class C(Base):
def __init__(self):
Base.__init__(self)
def d(self):
return "I'm C.d"
return C
def make_factory(getbase, make_cls):
def factory(classname):
if not classname in factory.cache:
Base = getbase(classname)
factory.cache[classname] = make_cls(Base, classname)
return factory.cache[classname]()
factory.cache = {}
return factory
AorB = make_factory(lambda classname: globals()[classname], make_AorB)
C = make_factory(lambda classname: AorB.cache[classname], make_C)
if __name__ == "__main__":
a = AorB("A")
print(a.__class__, a.__class__.__bases__)
print("A.a:", a.a())
print("A.b:", a.b())
b = AorB("B")
print(b.__class__, b.__class__.__bases__)
print("B.a:", b.a())
print("B.c:", b.c())
c = C("B")
print(c.__class__, c.__class__.__bases__)
print("C.a:", c.a())
print("C.c:", c.c())
print("C.d:", c.d())
yields
A
(<class '__main__.AorB'>, (<class '__main__.A'>,))
('A.a:', "I'm A.a")
('A.b:', "I'm A.b")
B
(<class '__main__.AorB'>, (<class '__main__.B'>,))
('B.a:', "I'm B.a")
('B.c:', "I'm B.c")
B
(<class '__main__.C'>, (<class '__main__.AorB'>,))
('C.a:', "I'm B.a")
('C.c:', "I'm B.c")
('C.d:', "I'm C.d")
3 Comments
a = AorB("A") and then a2 = AorB("A"), then a and a2 will be instances of the same class. If there were no cache, then a and a2 would be instances of different classes (although both classes would be named AorB.I'm not 100% sure (of your use-case), but you may be able to use the variation of type to do something like the following (where something is some conditional):
def produce_C(kls, *args, **kwdargs):
return type('C', (globals()[kls],), {})(*args, **kwdargs)
Which is going to confuse the type system though... (possibly amend the class name to be C_from_A or C_from_B - but ugh)
Comments
Here is the problem with your code (after the update) C can't inherit from AorB, which is a function:
class C(AorB):
def __init__(self, classname):
AorB.__init__(classname)
Since you want to bass the baseclass in a call to C, you can just make C a function that calls AorB in turn:
def C(basename):
return AorB(basename)