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:
是否有一种不错的pythonic方法可以通过函数调用动态创建这些类,该函数调用将新类放入当前范围,如:
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
.
因为我会需要这样的注释和问题:派生类都具有完全相同的内部结构,区别在于构造函数采用了许多以前未定义的参数。因此,例如,MyClass使用关键字一段时间,类TestClass的构造函数采用b和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.
我得到了这个工作,但更喜欢另一种方式,即动态创建的类对象。
2 个解决方案
#1
102
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:
这段代码允许您使用动态名称和参数名称创建新类。 __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
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" -
我看到你要求在命名范围中插入动态名称 - 现在,这在Python中不被认为是一种好习惯 - 你要么具有变量名称,在编码时已知,要么在数据中 - 在运行时学到的名称更多“数据“比”变量“ -
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:
如果你的设计绝对需要名称进入范围,只需执行相同的操作,但使用globals()调用返回的字典而不是任意字典:
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).
(类工厂函数确实可以在调用者的全局范围内动态插入名称 - 但这更糟糕的做法,并且在Python实现中不兼容。这样做的方法是获取调用者的执行帧,通过sys._getframe(1)并在其f_globals属性中设置框架的全局字典中的类名称。
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:
更新,tl;博士:这个答案已经变得流行,仍然是对问题机构非常具体的。关于如何在Python中“从基类动态创建派生类”的一般答案是对类型传递新类名的简单调用,一个带有基类的元组和一个用于新类的__dict__体 - 如下所示:
>>> 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.
更新任何需要这个的人也应该检查dill项目 - 它声称能够像pickle一样对普通对象进行pickle和unpickle类,并且在我的一些测试中已经活过了它。
#2
65
type()
is the function that creates classes (and in particular sub-classes):
type()是创建类(特别是子类)的函数:
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
#1
102
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:
这段代码允许您使用动态名称和参数名称创建新类。 __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
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" -
我看到你要求在命名范围中插入动态名称 - 现在,这在Python中不被认为是一种好习惯 - 你要么具有变量名称,在编码时已知,要么在数据中 - 在运行时学到的名称更多“数据“比”变量“ -
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:
如果你的设计绝对需要名称进入范围,只需执行相同的操作,但使用globals()调用返回的字典而不是任意字典:
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).
(类工厂函数确实可以在调用者的全局范围内动态插入名称 - 但这更糟糕的做法,并且在Python实现中不兼容。这样做的方法是获取调用者的执行帧,通过sys._getframe(1)并在其f_globals属性中设置框架的全局字典中的类名称。
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:
更新,tl;博士:这个答案已经变得流行,仍然是对问题机构非常具体的。关于如何在Python中“从基类动态创建派生类”的一般答案是对类型传递新类名的简单调用,一个带有基类的元组和一个用于新类的__dict__体 - 如下所示:
>>> 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.
更新任何需要这个的人也应该检查dill项目 - 它声称能够像pickle一样对普通对象进行pickle和unpickle类,并且在我的一些测试中已经活过了它。
#2
65
type()
is the function that creates classes (and in particular sub-classes):
type()是创建类(特别是子类)的函数:
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