2

I am trying to pass the type of a class to a method so that it can be dynamically instantiated. The class extends to a base class which further extends to an abstract class. Now when I check the type of my class it comes as the abstract class type instead of the child class.

Here is how my classes look like

class AMeta(type):
     # stuff

class Parent(six.with_metaclass(AMeta, object)):
     # stuff

class Child(Parent):
    # stuff

Now when I use type(Child) or Child.__class__ it gives me AMeta whereas I would like to get Child. I want to pass this Child to another method which would dynamically create its object.

def create_obj(clzz):
   return clzz()

When I call the method like create_obj(type(Child)) it doesn't work and breaks but when I call Child.mro()[0] it works fine what is happening here and is there another way to achieve what I am achieving via mro method?

Manthan Jamdagni
  • 894
  • 2
  • 17
  • 31
  • So you want `create_obj(Child)`? Is that it? Did I understand this right? (`Child.mro()[0]` is `Child` by the way.) – Aran-Fey Jul 04 '18 at 17:54
  • Yes I would like to do that only, I could also use type(name, bases, dict) to create the class dynamically but I would prefer the 1st approach. – Manthan Jamdagni Jul 04 '18 at 17:58

2 Answers2

4

A class is an instance of its metaclass. Ergo:

  • The type of Child is AMeta
  • The type of a Child() is Child
wim
  • 338,267
  • 99
  • 616
  • 750
  • Hi @wim thanks for the quick response, but I cannot directly create Child() as it requires few mandatory args while instantiating whose value I retrieve in the create_obj method is there any other way to achieve the same? – Manthan Jamdagni Jul 04 '18 at 18:01
  • Pass the arguments into `create_obj` and use them in the call `clzz(...)` – wim Jul 04 '18 at 18:03
  • @wim In other words: Get rid of the `create_obj` function because it's useless? – Aran-Fey Jul 04 '18 at 18:04
  • @Aran-Fey Presumably, it has some extra logic in it, or is used from a higher level of abstraction. – wim Jul 04 '18 at 18:10
2

If you do type(Child), you are asking what is the type of your Child class. Keep in mind that classes are also instances in Python. When in your script you do class Child..., there is a new name (Child) added to the script's namespace (pretty much a variable called Child of type AMeta since you're specifying that AMeta is the metaclass of Child. Otherwise, it would be of type type which is a bit like the "default" metaclass)

See:

import six

class AMeta(type):
     pass

class Parent(six.with_metaclass(AMeta, object)):
     pass

class Child(Parent):
    pass

print(type(Child))
c=Child()
print(type(c))

In the first print, you get <class '__main__.AMeta'> because you're asking What is the type of my Child instance?. In the second print you get <class '__main__.Child'> because you're asking What is the type of my c instance?

You don't need to do type(Child) to get the class. You can use it directly. For instance:

obj = Child
dynamic_instance = obj()
print(type(dynamic_instance))

Will print <class '__main__.Child'>

Closer to you example, that'd be:

def create_obj(clzz):
   return clzz()

a = create_obj(Child)
print("Just created: %s" % type(a))

Which outputs Just created: <class '__main__.Child'>

Savir
  • 17,568
  • 15
  • 82
  • 136