113

例如我有一个基类如下:

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采用band c

inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")

但他们不应该使用类的类型作为输入参数,比如

inst1 = BaseClass(classtype = "My", a=4)

我得到了这个工作,但更喜欢另一种方式,即动态创建的类对象。

4

3 回答 3

166

这段代码允许您创建具有动态名称和参数名称的新类。里面的参数验证是__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 对普通对象所做的那样腌制和取消腌制类,并且在我的一些测试中已经做到了。

于 2013-03-06T12:55:00.200 回答
104

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
于 2013-03-06T12:19:39.837 回答
-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*
于 2019-10-10T14:57:21.197 回答