Python类继承:使用自己的和父的默认值和init方法进行动态子类初始化

2024-09-30 04:33:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图创建一个类系统,这些类可以用**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

所以在设置了几个实例之后:

^{pr2}$

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

  • Gen2item1.p_var_1是“我的名字”
  • 第2.2条
  • Gen2item1.G1_var_1是“第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)

每个生成(级别)设置三次,并且不设置G1_var或{}值。对于Gen2item2,__init__的所有三代都将G2的变量1设置为666,G2的变量设置为(10,4)。在


因此,底线问题是如何为每个类生成生成一些“默认”字段(dict),以便:

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

然后,父对象以相同的方式为子类的同一实例设置由其生成的dict定义的默认值,调用并传递给它自己的父类的__init__,以此类推,直到父对象是{},在这种情况下,super().__init__不会被调用/传递参数。
我读过一些关于元类的文章,它们看起来有点过火,而got my hopes high on this有一个单独的父类和子类实例。
有没有一种方法可以在没有元类的情况下实现这样的功能?将默认值存储在dicts的外部全局dict中会更好(在这种情况下,如果能提供一些寻址方面的帮助,会很感激),还是有办法建立一个工厂?在


解决编辑:
__init_subclass__可以工作,并且几乎可以实现我想要的功能,前提是它还为要初始化的类定义了__init__(我项目的具体细节)。这是对我的问题的回答,并提供了一些有用的信息,如何实现我想要在我的项目设置。我相信只要定义一个类方法来设置一个适当的__init__并在定义类时让它运行(这正是最新的__init_subclass__似乎很有帮助)就足够了,但是至少要有一些向后兼容性,似乎元类就是这样做的方法。在


Tags: andkeyinselfforobjectinitvar
2条回答

我会跳过DEFAULTS这句话,因为这是不必要的膨胀。如果您真的需要访问这些参数的默认值,可以通过内省__init__方法来获取它们。否则,使用super的一般规则是去掉类特有的参数,并将所有其他参数传递给下一个对__init__的调用。在

类设计器的责任是确保在调用object.__init__之前,结果MRO中的任何类删除__init__的所有参数。在下面的示例中,Parent.__init__将使用实际参数调用object.__init__的唯一方法是,self的MRO中的某个其他类未能删除其参数。在

class Parent(object):
    def __init__(self, p_var_1="my name", p_var_2=2, **kwargs):
        super().__init__(**kwargs)
        self.p_var_1 = p_var_1
        self.p_var_2 = p_var_2

class Gen1(Parent): #should have p_var and G1_var fields
    def __init__(self, g1_var_1="Generation 1", g1_var_2=None, **kwargs):
        super().__init__(**kwargs)
        if g1_var_2 is None:
            g1_var_2 = []
        self.g1_var_1 = g1_var_1
        self.g1_var_2 = g1_var_2

class Gen2(Gen1): #should have p_var, G1_var and G2_var fields
    def __init__(self, g2_var_1=1337, g2_var_2=(10,4), **kwargs):
        super().__init__(**kwargs)
        self.g2_var_1 = g2_var_1
        self.g2_var_2 = g2_var_2

考虑打个电话

^{pr2}$

第一个调用的方法是Gen2.__init__,它只将p和{}变量传递给super.__init__。该调用导致对Gen1.__init__的调用,该调用只传递p个变量。调用Parent.__init__时,**kwargs为空。在

可以使用Python3.6中添加到object()的新类方法:^{}

当类是子类时调用此方法,并允许您在不使用元类的情况下自定义子类的创建。在

您的示例如下所示:

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

相关问题 更多 >

    热门问题