How can I dynamically create derived classes from a base class
例如,我有一个基类,如下所示:
1 2 3 | class BaseClass(object): def __init__(self, classtype): self._type = classtype |
我从这个类派生出其他几个类,例如
1 2 3 4 5 6 7 | class TestClass(BaseClass): def __init__(self): super(TestClass, self).__init__('Test') class SpecialClass(BaseClass): def __init__(self): super(TestClass, self).__init__('Special') |
有没有一种好的、Python式的方法可以通过一个函数调用动态地创建这些类,该函数调用将新类放入我当前的范围,比如:
1 2 3 | foo(BaseClass,"My") a = MyClass() ... |
因为会有注释和问题,所以我需要这样做:派生类都具有完全相同的内部结构,但不同之处在于,构造函数接受了许多以前未定义的参数。例如,
1 2 3 | inst1 = MyClass(a=4) inst2 = MyClass(a=5) inst3 = TestClass(b=False, c ="test") |
但它们不应该像这样使用类的类型作为输入参数
1 | inst1 = BaseClass(classtype ="My", a=4) |
我让它工作了,但更喜欢另一种方式,即动态创建类对象。
这段代码允许您使用动态名称和参数名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
它的工作原理是这样的,例如:
1 2 3 4 5 6 7 8 9 | >>> 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 |
我看到您要求在命名范围中插入动态名称--现在,这在Python中被认为不是一个好的实践——您也有变量名,在编码时已知,或在运行时学习的数据和名称更多的是"数据"而不是"变量"-
因此,您可以将类添加到字典中,然后从中使用它们:
1 2 3 4 | name ="SpecialClass" classes = {} classes[name] = ClassFactory(name, params) instance = classes[name](...) |
如果你的设计真的需要名字做同样的事情,但是使用
1 2 3 | name ="SpecialClass" globals()[name] = ClassFactory(name, params) instance = SpecialClass(...) |
(类工厂函数确实可以在调用者的全局范围内动态地插入名称——但这是更糟糕的做法,并且在Python实现中不兼容。这样做的方法是通过sys.get frame(1)获取调用方的执行帧,并在帧的
更新,tl;dr:这个答案已经很流行了,仍然是针对问题主体的。关于如何"从基类动态创建派生类"在python中,是对
1 | >>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...}) |
更新任何需要这个的人都应该检查DILL项目——它声称能够像pickle对普通对象那样对类进行pickle和unpickle操作,并且在我的一些测试中一直使用它。
1 2 3 4 5 6 7 8 9 10 | 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 |