如何从基类动态创建派生类 [英] How can I dynamically create derived classes from a base class

查看:28
本文介绍了如何从基类动态创建派生类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如我有一个基类如下:

class BaseClass(object):def __init__(self, classtype):self._type = 类类型

我从这个类派生了几个其他类,例如

class TestClass(BaseClass):def __init__(self):super(TestClass, self).__init__('Test')类特殊类(基类):def __init__(self):super(TestClass, self).__init__('Special')

是否有一种不错的 Pythonic 方法可以通过将新类放入当前范围的函数调用来动态创建这些类,例如:

foo(BaseClass, "My")a = MyClass()...

因为会有评论和问题,为什么我需要这个:派生类都具有完全相同的内部结构,不同之处在于构造函数采用许多以前未定义的参数.因此,例如,MyClass 采用关键字 a 而类 TestClass 的构造函数采用 bc.

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

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

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

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

解决方案

这段代码允许你用动态创建新的类名称和参数名称.__init__ 中的参数校验是不允许的未知参数,如果您需要其他验证,例如类型,或者它们是强制性的,只需添加逻辑那里:

class BaseClass(object):def __init__(self, classtype):self._type = 类类型def ClassFactory(name, argnames, BaseClass=BaseClass):def __init__(self, **kwargs):对于键,kwargs.items() 中的值:# 这里,argnames 变量是传递给# 类工厂调用如果键不在 argnames 中:raise TypeError("参数 %s 对 %s 无效"% (key, self.__class__.__name__))setattr(自我,键,值)BaseClass.__init__(self, name[:-len("Class")])newclass = type(name, (BaseClass,),{"__init__": __init__})返回新班级

这就像这样,例如:

<预><代码>>>>SpecialClass = ClassFactory("SpecialClass", "a b c".split())>>>s = SpecialClass(a=2)>>>s.a2>>>s2 = SpecialClass(d=3)回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中文件",第 8 行,在 __init__ 中类型错误:参数 d 对 SpecialClass 无效

我看到您要求在命名范围中插入动态名称-现在,在 Python 中不被认为是一个好习惯——你要么有变量名称,在编码时已知,或数据 - 以及在运行时学习的名称比变量"更多的数据" -

因此,您可以将您的类添加到字典中并从那里使用它们:

name = "SpecialClass"类 = {}类[名称] = 类工厂(名称,参数)实例 = 类[名称](...)

如果您的设计绝对需要名称在范围内,做同样的事情,但使用 globals()<返回的字典/代码>调用而不是任意字典:

name = "SpecialClass"globals()[name] = ClassFactory(name, params)实例 = SpecialClass(...)

(类工厂函数确实可以在调用者的全局范围内动态插入名称 - 但这是更糟糕的做法,并且在 Python 实现之间不兼容.这样做的方法是通过 sys._getframe(1) 获取调用者的执行框架并在其 f_globals 属性中设置框架全局字典中的类名).

update, tl;dr: 这个答案已经很流行,但它仍然非常特定于问题主体.关于如何的一般答案从基类动态创建派生类"在 Python 中是对 type 的简单调用,传递新类名、带有基类的元组和新类的 __dict__ 主体 - 像这样:<预><代码>>>>new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})

更新
任何需要它的人还应该检查 dill 项目 - 它声称能够腌制和取消腌制类就像泡菜对普通对象所做的那样,并且在我的一些测试中一直坚持下去.

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:

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.

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.

解决方案

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:

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" -

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:

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).

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:

>>> 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.

这篇关于如何从基类动态创建派生类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆