Kirill Cherepanov
Kirill Cherepanov

Reputation: 977

Use class instance as parent class in python

I'm trying to get a better understanding of python class system. This question is intended only to satisfy my curiosity.

Is it possible to use somehow a class instance as a parent class for another class. So far what I tried to do is

class A:
    pass

a = A()

class B(a):
    pass

and it gives following error: TypeError: object() takes no parameters

class Meta(type):
    pass

class A:
    pass

a = A()

class B(a, metaclass=Meta):
    pass

and it gives this error TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

I'm wondering, is it possible to somehow proxy all of the class instance methods to metaclass, so my instance would behave as a class

Upvotes: 0

Views: 201

Answers (4)

FHTMitchell
FHTMitchell

Reputation: 12157

EDIT:

OK, so it is possible to hack if you have control over the base class (either using metaclasses or __init_subclass__ (python 3.6 or later))

class A:
    x = 1
    def __init_subclass__(cls, inst, **kwargs):
        super().__init_subclass__(**kwargs)
        for k, v in vars(inst).items():
            if k not in dir(cls):  # i.e. don't override but remove if you want to
                setattr(cls, k, v)

a = A()
a.y = 2   

class B(A, inst=a):
    pass

B.x # --> 1
B.y # --> 2

You're sort of on the right tracks with your second example. Yes it is possible.

class Meta(type): pass
class A(metaclass=Meta): pass
class B(A): pass

issubclass(B, A) # --> True
isinstance(B, Meta) # --> True

Thats because A is an instance of Meta (thats what a metaclass means, a class whose instance is also a class). Hence B is a subclass of an instance of Meta.

So yes, you could set for example

class Meta(type):
    def __init__(cls, *args, **kwargs):
         cls.x = 1
         super().__init__(cls, *args, **kwargs)

class A(Meta):
    def __init__(self):
         self.y = 2

A.x  # 1 (acts as an instance of Meta)
a = A()
a.y  # 2 (acts as an instance of A)
a.x  # AttributeError (does not act as an instance of Meta)

Upvotes: 0

bruno desthuilliers
bruno desthuilliers

Reputation: 77892

Is it possible to use somehow a class instance as a parent class for another class

Well, actually that's always the case since classes ARE instances (of their metaclass). But then the class of the instance you want to use as a parent class (yeah, re-read it thrice...) must behave as if it was a genuine metaclass - which can rapidily become impractical.

I'm wondering, is it possible to somehow proxy all of the class instance methods to metaclass, so my instance would behave as a class

There might be a way indeed (overriding __getattr__() or __getattribute__()), but devil is in the details and chances are that by the time you make this rube goldberg contraption "kind-of-work-with-severe-limitations-and-corner-cases", you will wonder if that was really worth the pain.

Note that OTHO both composition/delegation and dynamic (runtime) creation / modification of classes is absurdly easy in Python so I can hardly think of a problem that would be better solved by (ab)using an instance as a parent class.

Upvotes: 1

zwer
zwer

Reputation: 25789

Your instance needs to be a type in order to be used in inheritance chains. For example:

class A(type):
    pass

a = A(str)  # you need to pass the type's `type` to the construcor

class B(a):
    pass

That being said, there's little, if no practical application for this that I can think of - or rather whatever you achieve this way will be easier and more Pythonic to achieve through normal inheritance and metaclasses.

Upvotes: 0

Matias Cicero
Matias Cicero

Reputation: 26281

Every instance has a __class__ attribute that refers to the underlying class structure.

Knowing this, you can actually do the following:

class A(object):
   def foo(self):
      return 'hello'

a = A()

class B(a.__class__):
    pass

b = B()
print(b.foo()) # Prints 'hello'

Alternatively, you can also use type:

class B(type(a)):
    pass

b = B()
print(b.foo()) # Prints 'hello'

Upvotes: 0

Related Questions