例如我有一个基类如下:
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')
是否有一种不错的 Pythonic 方法可以通过将新类放入我当前范围的函数调用动态创建这些类,例如:
foo(BaseClass, "My")
a = MyClass()
...
因为会有评论和问题,为什么我需要这个:派生类都具有完全相同的内部结构,不同之处在于构造函数采用了许多以前未定义的参数。因此,例如,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)
我得到了这个工作,但更喜欢另一种方式,即动态创建的类对象。
a
它总是 MyClass
而 TestClass
永远不会接受 a
?为什么不在 BaseClass.__init__()
中声明所有 3 个参数,而是将它们全部默认为 None
? def __init__(self, a=None, b=None, C=None)
?
这段代码允许您创建具有动态名称和参数名称的新类。 __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;dr: 这个答案已经很流行,但它仍然非常具体到问题主体。关于如何在 Python 中“从基类动态创建派生类” 的一般答案是对 type
的简单调用,传递新的类名、带有基类的元组和 { 2} 新类的主体 - 像这样:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
更新
任何需要这个的人也应该检查 dill 项目 - 它声称能够像 pickle 对普通对象所做的那样腌制和取消腌制类,并且在我的一些测试。
type()
是创建类,特别是子类的函数,如问题中所示:
def set_x(self, value):
self.x = value
# type() takes as argument the new class name, its base
# classes, and its attributes:
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
TypeError
,上面写着 __init__() takes exactly 2 arguments (1 given)
。我发现添加一些东西(任何东西?)来填补空白就足够了。例如,obj = SubClass('foo')
运行没有错误。
SubClass
是问题中 BaseClass
的子类,并且 BaseClass
带有一个参数(classtype
,在您的示例中为 'foo'
)。
super()
,但 Python 解释器不能再发挥它的魔力并直接解释裸 super()
,因此您必须在 set_x
(或 SubClass
的任何其他方法)中使用更明确的形式 { 5}。
要创建具有动态属性值的类,请查看以下代码。注意。这是python编程语言中的代码片段
def create_class(attribute_data, **more_data): # define a function with required attributes
class ClassCreated(optional extensions): # define class with optional inheritance
attribute1 = adattribute_data # set class attributes with function parameter
attribute2 = more_data.get("attribute2")
return ClassCreated # return the created class
# use class
myclass1 = create_class("hello") # *generates a class*
不定期副业成功案例分享
BaseClass.__init__()
作为更通用的super(self.__class__).__init__()
会更好,当新类被子类化时,它会更好地发挥作用。 (参考:rhettinger.wordpress.com/2011/05/26/super-considered-super)super
替换它并创建一个动态创建的类的子类来理解它;另一方面,在这种情况下,您可以将基类作为调用__init__
的通用对象。BaseClass
的__init__
是用一个参数调用的,但实际上BaseClass.__init__
总是采用任意的关键字参数列表。其次,上面的解决方案将所有允许的参数名称设置为属性,这不是我想要的。任何参数都必须转到BaseClass
,但在创建派生类时我知道哪个参数。我可能会更新问题或提出更精确的问题以使其更清楚。super()
给出TypeError: must be type, not SubSubClass
。如果我理解正确,这来自__init__()
的第一个参数self
,它是一个SubSubClass
,其中需要一个type
对象:这似乎与super(self.__class__)
是一个未绑定的超级对象这一事实有关。它的__init__()
方法是什么?我不确定哪种方法可能需要type
类型的第一个参数。你能解释一下吗? (旁注:我的super()
方法在这里确实没有意义,因为__init__()
有一个变量签名。)