4

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")

3
  • Why would you want dynamic inheritance? Commented 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? Commented 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") Commented Mar 5, 2013 at 15:19

3 Answers 3

6
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")
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, for which purpose is the 'cache' part?
How can I inherit the AorB class? See update in original post
The purpose of the cache is to reuse the same classes if they have already been created. For example, if you say 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.
0

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

0

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.