我正在尝试创建一个可以用** kwargs初始化并具有每个字段默认值的类的系统。这是代表我要执行的操作的一些代码:

class Parent(object): #should only have p_var fields
    DEFAULTS = {
    "p_var_1" : "my name",
    "p_var_2" : 2
    }

    def __init__(self, **kwargs):
        for key in DEFAULTS:
            #Also include check for list type and copy, not assign
            if key in kwargs:
                setattr(self, key, kwargs[key])
            else:
                setattr(self, key, self.DEFAULTS[key])
        #Check for super being 'object' - problems?
        #if not super() == object:
        #   super().__init__(**kwargs)


class Gen1(Parent): #should have p_var and G1_var fields
    DEFAULTS = {
    "G1_var_1" : "Generation 1",
    "G1_var_2" : []
    }

    #redefining only in case if Parent can't check for super
    def __init__(self, **kwargs):
        for key in DEFAULTS:
            #Also include check for list type and copy, not assign
            if key in kwargs:
                setattr(self, key, kwargs[key])
            else:
                setattr(self, key, self.DEFAULTS[key])

        #check whether super() is object
        super().__init__(**kwargs) #pass all args to parent init

class Gen2(Gen1): #should have p_var, G1_var and G2_var fields
    DEFAULTS = {
    "G2_var_1" : 1337,
    "G2_var_2" : (10,4)
    }

    #After Gen1, redefining __init__ shouldn't be necessary


这样,在设置几个实例之后:

Gen2item1 = Gen2()
Gen2item2 = Gen2(p_var_1 = "different name", G2_var_1 = 666)


Gen2item1将初始化为所有默认值:


Gen2item1.p_var_1是“我的名字”
Gen2item1.p_var_2是2
Gen2item1.G1_var_1是“ Generation 1”
Gen2item1.G1_var_2为[](副本)
Gen2item1.G2_var_1为1337,
Gen2item1.G2_var_2为(10,4)


但是Gen2item1的p_var_1和G2_var_1值将不同,其余的相同。
但是,由于在子类中覆盖了“ DEFAULT”,因此Gen2item1的值如下:


Gen2item1.p_var_1是666
Gen2item1.p_var_2为(10,4)


每个世代(级别)都设置了3次,并且未设置G1_varp_var值。对于Gen2item2,所有三代__init__均将G2_var_1设置为666,将G2_var_2设置为(10,4)。



因此,最重要的问题是关于为每个类生成一个“默认”字段(dict)的方法,以便:


通过__init__调用子类的item = subclass()时,它将根据'defaults'dict的键从kwargs中获取参数,
通过setattrib(self, key, value)设置自己的字段,如果存在来自默认值的键,则从kwargs中获取值,否则从默认值中获取值,并且
通过super()__init__(**kwargs)将kwarg传递给其父级时,调用其父级的init。


然后,父级对子类的相同实例以相同的方式设置由其生成的dict定义的默认值,调用并传递给其父级的__init__,依此类推,直到父级为object,在这种情况下,根本不会被调用/传递参数。
我已经阅读了有关元类的信息,这些元类似乎过大了,还有got my hopes high on this,它有一个单独的父类和子类实例。
没有元类,有没有办法实现这样的事情?将默认值存储在外部全局dict中会更好(在这种情况下,将对寻址有所帮助),或者是否有办法建立工厂?



解决了编辑:
super().__init__可以工作,并且几乎可以满足我的要求,只要它也为要初始化的类(我的项目的详细信息)定义了__init_subclass__即可。这是对我的问题的解答,并提供了一些有用的信息,说明如何在项目中实现所需的设置。我相信定义一个建立适当的__init__并在定义类时运行它的类方法就足够了(这是非常新的__init__似乎有帮助的方法),但是至少要有一些向后的方法-compatibility,看来元类是做到这一点的方法。

最佳答案

您可以使用在Python 3.6中添加到object()的新类方法:__init_subclass__

当类被子类化时,将调用此方法,该方法使您无需使用元类就可以自定义子类的创建。

您的示例如下所示:

class Base:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__()
        for key, value in kwargs.items():
            setattr(cls, key, value)

    def __init__(self, *args, **kwargs):
        super().__init__()
        for key, value in kwargs.items():
            setattr(self, key, value)

class Parent(Base, p_var_1='my_name', p_var_2=2):
    pass

class Gen1(Parent, G1_var_1='Generation 1', G1_var_2=[]):
    pass

class Gen2(Gen1, G2_var_1=1337, G2_var_2=(10,4)):
    pass

08-19 21:43