例如,我有一个如下的基类:
class BaseClass(object): def __init__(self, classtype): self._type = classtype
从这个类中,我可以得出其他几个类,例如
class TestClass(BaseClass): def __init__(self): super(TestClass, self).__init__('Test') class SpecialClass(BaseClass): def __init__(self): super(TestClass, self).__init__('Special')
有没有一种不错的python方式通过函数调用动态创建这些类,该函数将新类放入我的当前作用域,例如:
foo(BaseClass, "My") a = MyClass() ...
由于会有评论和疑问,为什么我需要这样做:派生类都具有完全相同的内部结构,不同之处在于,构造函数采用了许多以前未定义的参数。因此,例如,MyClass采用关键字,a而类的构造函数TestClass采用b和c。
MyClass
a
TestClass
b
c
inst1 = MyClass(a=4) inst2 = MyClass(a=5) inst3 = TestClass(b=False, c = "test")
但是他们绝不应该将类的类型用作输入参数,例如
inst1 = BaseClass(classtype = "My", a=4)
我使它起作用,但希望采用其他方式,即动态创建的类对象。
这一段代码使您可以使用动态名称和参数名称创建新的类。参数验证__init__只是不允许使用未知参数,如果您需要其他验证(例如类型),或者它们是必需的,则只需在其中添加逻辑即可:
__init__
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
这样,例如:
>>> 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中不被认为是一种好习惯-您要么具有在编码时已知的变量名,要么在数据中拥有- 在运行时学习的名称更多”数据”而不是“变量”-
因此,您可以将您的类添加到字典中,然后从那里使用它们:
name = "SpecialClass" classes = {} classes[name] = ClassFactory(name, params) instance = classes[name](...)
而且,如果您的设计绝对需要将名称包含在范围内,请执行相同的操作,但使用globals() 调用返回的字典而不是任意字典:
globals()
name = "SpecialClass" globals()[name] = ClassFactory(name, params) instance = SpecialClass(...)
(对于类工厂函数,确实有可能在调用者的全局范围内动态插入名称- 但这是更糟糕的做法,并且在Python实现之间不兼容。这样做的方法是获取调用者的执行框架,方法是通过sys._getframe(1)并在其f_globals属性的框架的全局字典中设置类名称。
f_globals
更新,tl;博士: 这个答案已经很流行了,但仍然非常适合问题主体。关于如何 在Python中 “从基类动态创建派生类” 的一般答案 是一个简单的调用,即type传递新的类名,带有基类的元组和__dict__新类的主体,如下所示:
type
__dict__
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
更新 需要此 功能的 任何人都还应该检查莳萝项目- 它声称能够像腌制普通对象一样对类进行腌制和腌制,并且在我的一些测试中得到了证实。