Python类继承:使用自己和父默认值以及init方法

时间:2017-09-06 15:49:02

标签: python oop inheritance subclass

我正在尝试创建一个类系统,可以使用** 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是“第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_varp_var值。对于Gen2item2,G2_var_1被__init__的所有三代设置为666并且G2_var_2设置为(10,4)。

所以底线问题是关于为每个类生成一些'默认'字段(dict)的方法,以便:

  1. 通过__init__调用子类的item = subclass()时,它会从kwargs获取参数,具体取决于'默认'字典的键,
  2. 通过setattrib(self, key, value)设置自己的字段,如果默认值中存在键,则从kwargs中获取值,否则从默认值中获取值,
  3. 通过super()__init__(**kwargs)将kwargs传递给它时,调用其父级的init。
  4. 父节点然后设置由其生成的dict定义的默认值,对于子类的相同实例,调用并传递给它自己的父节点__init__,依此类推,直到父节点为object为止。 ,在这种情况下,super().__init__根本不会被调用/传递参数 我读过有关元类的内容,它看起来有点过分,而got my hopes high on this则有一个单独的父类和子类实例。
    有没有办法在没有元类的情况下实现这样的事情?将默认值存储在dicts的外部全局字典中是否更好(在这种情况下,可以理解一些寻址的帮助),或者有没有办法建立工厂?

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

2 个答案:

答案 0 :(得分:1)

您可以在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

答案 1 :(得分:0)

我会跳过DEFAULTS这些不必要的臃肿。如果您确实需要访问这些参数的默认值,可以通过内省__init__方法来获取它们。否则,使用super的一般规则是删除您的类中唯一的参数,并将所有其他参数传递给下一次调用__init__

类设计师有责任确保在__init__被调用之前,生成的MRO中的任何类都删除object.__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

考虑像

这样的电话
Gen2(p_var_1=...,
     p_var_2=...,
     g1_var_1=...,
     g1_var_2=...,
     g2_var_1=...,
     g2_var_2=...)

第一个调用的方法是Gen2.__init__,它只会将pg1个vars传递给super.__init__。该调用导致调用Gen1.__init__,该调用仅传递p个变量。调用Parent.__init__时,**kwargs为空。