Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I dynamically create derived classes from a base class

For example I have a base class as follows:

class BaseClass(object):     def __init__(self, classtype):         self._type = classtype 

From this class I derive several other classes, e.g.

class TestClass(BaseClass):     def __init__(self):         super(TestClass, self).__init__('Test')  class SpecialClass(BaseClass):     def __init__(self):         super(TestClass, self).__init__('Special') 

Is there a nice, pythonic way to create those classes dynamically by a function call that puts the new class into my current scope, like:

foo(BaseClass, "My") a = MyClass() ... 

As there will be comments and questions why I need this: The derived classes all have the exact same internal structure with the difference, that the constructor takes a number of previously undefined arguments. So, for example, MyClass takes the keywords a while the constructor of class TestClass takes b and c.

inst1 = MyClass(a=4) inst2 = MyClass(a=5) inst3 = TestClass(b=False, c = "test") 

But they should NEVER use the type of the class as input argument like

inst1 = BaseClass(classtype = "My", a=4) 

I got this to work but would prefer the other way, i.e. dynamically created class objects.

like image 946
Alex Avatar asked Mar 06 '13 12:03

Alex


People also ask

How do you create a dynamic class in Python?

Python Code can be dynamically imported and classes can be dynamically created at run-time. Classes can be dynamically created using the type() function in Python. The type() function is used to return the type of the object. The above syntax returns the type of object.

How do you create a dynamic function in Python?

Method 1: exec() Python's built-in exec() executes the Python code you pass as a string or executable object argument. This is called dynamic execution because, in contrast to normal static Python code, you can generate code and execute it at runtime. This way, you can run programmatically-created Python code.

How do you create an automatic object in Python?

Creating an Object of a Class in Python One can give any name to a newly created object. Object creation is similar to calling a function. This is because as soon as the object is created or instantiated with the class name, the default constructor of the class is called automatically.


1 Answers

This bit of code allows you to create new classes with dynamic names and parameter names. The parameter verification in __init__ just does not allow unknown parameters, if you need other verifications, like type, or that they are mandatory, just add the logic there:

class BaseClass(object):     def __init__(self, classtype):         self._type = classtype  def ClassFactory(name, argnames, BaseClass=BaseClass):     def __init__(self, **kwargs):         for key, value in kwargs.items():             # here, the argnames variable is the one passed to the             # ClassFactory call             if key not in argnames:                 raise TypeError("Argument %s not valid for %s"                      % (key, self.__class__.__name__))             setattr(self, key, value)         BaseClass.__init__(self, name[:-len("Class")])     newclass = type(name, (BaseClass,),{"__init__": __init__})     return newclass 

And this works like this, for example:

>>> SpecialClass = ClassFactory("SpecialClass", "a b c".split()) >>> s = SpecialClass(a=2) >>> s.a 2 >>> s2 = SpecialClass(d=3) Traceback (most recent call last):   File "<stdin>", line 1, in <module>   File "<stdin>", line 8, in __init__ TypeError: Argument d not valid for SpecialClass 

I see you are asking for inserting the dynamic names in the naming scope -- now, that is not considered a good practice in Python - you either have variable names, known at coding time, or data - and names learned in runtime are more "data" than "variables" -

So, you could just add your classes to a dictionary and use them from there:

name = "SpecialClass" classes = {} classes[name] = ClassFactory(name, params) instance = classes[name](...) 

And if your design absolutely needs the names to come in scope, just do the same, but use the dictionary returned by the globals() call instead of an arbitrary dictionary:

name = "SpecialClass" globals()[name] = ClassFactory(name, params) instance = SpecialClass(...) 

(It indeed would be possible for the class factory function to insert the name dynamically on the global scope of the caller - but that is even worse practice, and is not compatible across Python implementations. The way to do that would be to get the caller's execution frame, through sys._getframe(1) and setting the class name in the frame's global dictionary in its f_globals attribute).

update, tl;dr: This answer had become popular, still its very specific to the question body. The general answer on how to "dynamically create derived classes from a base class" in Python is a simple call to type passing the new class name, a tuple with the baseclass(es) and the __dict__ body for the new class -like this:

>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...}) 

update
Anyone needing this should also check the dill project - it claims to be able to pickle and unpickle classes just like pickle does to ordinary objects, and had lived to it in some of my tests.

like image 60
jsbueno Avatar answered Oct 21 '22 08:10

jsbueno