代码之家  ›  专栏  ›  技术社区  ›  spaderdabomb

从包含和不包含**Kwargs的类继承

  •  1
  • spaderdabomb  · 技术社区  · 8 年前

    我正在用python构建一个plotting类,希望能完成以下工作。我想要一个使用pyqt5的图形窗口,它也继承了我创建的一些自定义类(比如曲线拟合类)。为了让曲线拟合类操作Plotting类中持续存在的数据,它必须具有对Plotting类中包含的数据的引用。因此,我选择了plotting类来继承curvefiting类。

    问题似乎出现在继承Pyqt5的GraphicsWindow类和我的自定义类时,这两个类接受不同数量的参数。我读到python不适合使用“super”功能继承不同数量参数的类,所以我决定让我的自定义curvefitting类接受**kwargs,这样就可以给它一个对父类的引用。然而,我却遇到了另一个我不理解的错误。下面是一个整理好的例子

    import numpy as np
    from pyqtgraph import GraphicsWindow
    
    class ClassA():
        def __init__(self, **kwargs):
            super().__init__()
            self.kwargs = kwargs
            self.parent = self.kwargs['parent']
            self.xdata = self.parent.xdata
    
        def print_data(self):
            print(self.parent.xdata)
            print(self.parent.ydata)
    
    
    class classC(GraphicsWindow, ClassA):
        def __init__(self):
            kwargs = {}
            kwargs['parent'] = self
            kargs = kwargs
            self.xdata = np.linspace(0, 100, 101)
            self.ydata = np.linspace(0, 200, 101)
    
            super().__init__(**kwargs)
            # ClassA.__init__(self, **kwargs)
            # GraphicsWindow.__init__(self)
    
    instC = classC()
    instC.print_data()
    

    当我运行上面的代码时,我得到“runtimeerror:super-class 初始 在“super().\uu init(**kwargs)”行中,classc类型的()从未被称为“on”,我真的完全不明白这一点,并尝试了一段时间的谷歌搜索,但没有成功。

    另外,我尝试注释掉这一行,并取消注释下两行以手动从每个类继承,但这也不起作用。我觉得很奇怪的是,如果我把这两行中的一行注释掉,它们都是单独工作的,但一起却不工作。例如,如果我用这两行运行它,它会给我一个错误,即kwargs没有关键字'parent',好像它甚至没有通过**kwargs。

    有没有办法从两个采用不同数量的初始化参数的类继承?我有没有完全不同的方法来解决这个问题?谢谢。

    1 回复  |  直到 8 年前
        1
  •  2
  •   Blckknght    8 年前

    代码的直接问题是 ClassC 继承自 GraphicsWindow 作为它的第一个基类, ClassA 是第二个基类。当你打电话 super ,只有一个被调用( 图形窗口 )如果它不是设计用于多重继承(看起来是这样),它可能不会调用 超级的 自身或不可能传递 甲级 期待。

    仅仅改变基类的顺序就足以让它工作。python保证基类的调用顺序与它们在 class 语句(如果以后发生更多继承,则可以在MRO中的其他类之间插入)。自从 ClassA.__init__ 打电话来 超级的 ,应该效果更好!

    做起来可能很难 __init__ 方法使用多重继承,即使所有涉及的类都设计为使用它。这就是为什么位置参数经常被避免,因为它们的顺序可能变得非常混乱(因为子类只能在父类的位置参数之前添加位置参数,除非它们想重复所有的名称)。使用关键字参数绝对是一种更好的方法。

    但是,您所拥有的代码使处理关键字参数变得比应该的复杂一些。不需要显式地创建字典来传递 **kwargs 语法,也不需要从接受的dict中提取关键字值。 **夸尔格斯 争论。通常每个函数都应该命名它所接受的参数,并且只使用 **夸尔格斯 对于未知参数(mro中的其他类可能需要)。下面是它的样子:

    class Base1:
        def __init__(self, *, arg1, arg2, arg3, **kwargs):  # the * means the other args are kw-only
            super().__init__(**kwargs)                      # always pass on all unknown arguments
            ...                                             # use the named args here (not kwargs)
    
    class Base2:
        def __init__(self, *, arg4, arg5, arg6, **kwargs):
            super().__init__(**kwargs)
            ...
    
    class Derived(Base1, Base2):
        def __init__(self, *, arg2, arg7, **kwargs):         # child can accept args used by parents
            super().__init__(arg2=arg2+1, arg6=3, **kwargs)  # it can then modify or create from
            ...                                              # scratch args to pass to its parents
    
    obj = Derived(arg1=1, arg2=2, arg3=3, arg4=4, arg5=5, arg7=7) # note, we've skipped arg6
                                                                  # and Base1 will get 3 for arg2
    

    但我也会认真考虑遗产在你的情况下是否有意义。将两个基类中的一个封装在子类中,而不是继承自子类,这可能更有意义。也就是说,你只能从 甲级 或 图形窗口 ,并将另一个实例存储在 C类 是的。(您甚至可以从两个基类都继承,并将它们都封装起来。)封装通常比继承更容易推理和正确处理。

    推荐文章