Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python类继承:使用自身和父默认值以及init方法进行动态子类初始化_Python_Oop_Inheritance_Subclass - Fatal编程技术网

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

Python类继承:使用自身和父默认值以及init方法进行动态子类初始化,python,oop,inheritance,subclass,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:

我试图创建一个类系统,它可以用**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_变量_1是“第1代”
  • Gen2item1.G1_变量2为[](副本)
  • Gen2item1.G2_变量1为1337和
  • Gen2item1.G2_变量2为(10,4)
但是Gen2item1将具有不同的p_var_1和G2_var_1值,其余值相同。 但是,由于“默认值”在子类中被重写,因此Gen2item1的值为:

  • Gen2item1.p_var_1是666
  • Gen2item1.p_var_2是(10,4)
按每一代(级别)设置三次,并且未设置
G1\u var
p\u var
值。对于Gen2item2,所有三代
\uuuu init\uuu
都将G2变量1设置为666,将G2变量2设置为(10,4)


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

  • 通过
    item=subclass()
    调用子类的
    \uuuu init\uuuu
    时,它将根据“默认”dict的键从
    kwargs
    中获取参数
  • 通过
    setattrib(self,key,value)
    设置自己的字段,如果存在默认值中的键,则从kwargs获取值,否则也从默认值获取值,以及
  • 调用其父级的init,同时通过
    super()\uuuu init\uuuu(**kwargs)
    将kwargs传递给该父级 然后,父对象以同样的方式为子类的同一实例设置由其生成的dict定义的默认值,调用并传递给其自己的父对象的
    \uuuu init\uuuu
    ,依此类推,直到父对象是
    对象
    ,在这种情况下,
    超级()。\uuu init\uuu
    根本不会被调用/传递参数。
    我读过关于元类的文章,这似乎有些过分,而且它有一个单独的父类和子类实例。
    有没有一种方法可以在没有元类的情况下实现类似的功能?是否最好将默认值存储在外部全局dict of dict中(在这种情况下,需要一些寻址方面的帮助),还是有办法建立工厂


    已解决的编辑:

    \uuuuuu init\u子类\uuuuu
    可以工作,并且基本上满足了我的要求,前提是它还为要初始化的类定义了
    \uuuuuuuu init
    (我的项目的具体细节)。这是对我问题的回答,并为如何在项目中实现我想要的设置提供了一些有用的信息。我相信,定义一个classmethod就足够了,它可以设置一个合适的
    \uuuuuu init\uuuuu
    并在定义类时运行它(这是非常新的
    \uuuuuu init\u子类\uuuuuuu
    所做的),但至少要有一些向后兼容性,元类似乎是实现这一点的方法。

    我会跳过
    默认值作为不必要的膨胀。如果确实需要访问这些参数的默认值,可以通过内省
    \uuuu init\uuu
    方法来获取它们。否则,使用
    super
    的一般规则是去掉类特有的参数,并将所有其他参数传递给下一个对
    \uuuuuu init\uuuu
    的调用

    类设计器负责确保生成的MRO中的任何类在调用
    对象之前删除
    的所有参数。在下面的示例中,如果
    self
    的MRO中的某个其他类无法删除其参数,则
    Parent.\uuuuuu init\uuuu
    将使用实际参数调用
    对象.\uuuuuuuuu init\uuuu

    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.\uuuuu init\uuuu
    ,它只将
    p
    g1
    变量传递给
    super.\uuuuuu init\uuu
    。该调用导致对
    Gen1.\uuuu init\uuuu
    的调用,该调用只传递上的
    p
    变量。调用
    Parent.\uuuuu init\uuuuu
    时,
    **kwargs
    为空。

    您可以使用添加到Python 3.6中的
    对象()
    的新类方法:

    当类是子类时调用此方法,并允许您自定义子类的创建,而无需使用元类

    您的示例如下所示:

    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
    

    然而,我也希望在设置中有一些傻瓜式的证明,比如测试变量的类型,如果类型不合适,则恢复为默认值,或者忽略意外的关键字参数,最好不要为每个类创建大量的init,以便在添加大量参数的情况下使用。我现在尝试的方法是为每个类使用(外部)dict,一个用于默认值,另一个用于可接受类型的元组;如果使用无效,则应将其视为错误。至于意外的关键字参数,你不能仅仅因为你的类不需要参数就认为它不应该被传递到MRO中更远的类上。这可以用于设置默认值,但似乎我仍然必须定义
    \uuuuu init\uuuu
    来在创建实例时更改值,由于按原样使用此代码在执行类似于
    g1=Gen1(g1\u var\u 1=“asdf”)
    的操作时会抛出类型错误,在
    \uuuuuu init\uuuu子类中设置
    \uuuuu init\uuuuuu
    的creator函数,因此我想要做的似乎是可行的。在一些测试之后,如果没有问题出现,我们会将此标记为答案。是的,示例太简单了。它仅用于显示
    \uuuu init\u子类
    方法。你说得对:
    \uuuuu init\u subclass\uuuu
    被调用来创建subc