这段代码允许您创建具有动态名称和参数名称的新类。里面的参数验证是__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()
调用返回的字典而不是任意字典:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(类工厂函数确实可以在调用者的全局范围内动态插入名称 - 但这是更糟糕的做法,并且与 Python 实现不兼容。这样做的方法是获取调用者的执行框架,通过sys._getframe(1)并在其f_globals
属性中设置框架的全局字典中的类名)。
更新,tl;博士:这个答案已经变得流行,仍然非常具体到问题主体。关于如何
在 Python 中“从基类动态创建派生类”的一般答案
是一个简单的调用来type
传递新类名、一个带有基类的元组和__dict__
新类的主体 - 如下所示:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
更新
任何需要这个的人也应该检查dill项目 - 它声称能够像 pickle 对普通对象所做的那样腌制和取消腌制类,并且在我的一些测试中已经做到了。