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 get several other classes, for example

 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 good Putin way to dynamically create these classes by calling a function that puts a new class in my current scope, for example:

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

Since there will be comments and questions, why do I need it: derived classes have the same internal structure with the difference that the constructor takes a few earlier undefined arguments. For example, MyClass accepts the keywords a , and the constructor of the TestClass class accepts b and c .

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

But they should NEVER use a class type as an input argument, for example

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

I got this to work, but would prefer a different way, i.e. a dynamically created class object.

+82
python inheritance class
Mar 06 '13 at 12:13
source share
2 answers

This bit of code allows you to create new classes with dynamic names and parameter names. Checking parameters in __init__ just does not allow unknown parameters, if you need other checks, for example, type, or that they are required, 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 it works like this:

 >>> SpecialClass = ClassFactory("SpecialClass", "abc".split()) >>> s = SpecialClass(a=2) >>> sa 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 that you are asking to insert dynamic names in the naming field - now this is not considered good practice in Python - you either have variable names known at coding time or data - and names received at runtime have more "data" than "variables" -

So you can just add your classes to the dictionary and use them from there:

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

And if your design absolutely needs names that fall within the 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(...) 

(indeed, a factory class function could dynamically insert the name into the global scope of the caller, but this is even worse and not compatible with the Python implementation. to get the caller's execution frame, sys._getframe (1) and set the class name in the global frame dictionary to its attribute f_globals ).

update, tl; dr: This answer has become popular, but nonetheless very specific to the organ of the question. The general answer about how to "dynamically create derived classes from a base class" in Python is a simple type call to pass the new class name, a tuple with the base class (es) and the __dict__ tag for the new class - for example:

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

Update
Anyone who needs this should also check out the dill project - he claims to be able to chop and scatter classes, like pickling, on ordinary objects, and have survived in some of my tests.

+125
Mar 06 '13 at 12:55
source share

type() is a function that creates classes (and, in particular, subclasses):

 def set_x(self, value): self.x = value SubClass = type('SubClass', (BaseClass,), {'set_x': set_x}) # (More methods can be put in SubClass, including __init__().) obj = SubClass() obj.set_x(42) print obj.x # Prints 42 print isinstance(obj, BaseClass) # True 
+74
Mar 06 '13 at 12:19
source share



All Articles