繁体   English   中英

用多重继承在没有冗余代码的情况下用mixin处理Python覆盖类方法的正确方法是什么?

[英]What is the proper way to deal with Python overriding of class methods with a mixin in multiple inheritance without redundant code?

我希望我在理论上有一个小问题,并且有解决问题的正确方法。 我似乎更容易举例,然后解释,因为我的词汇似乎失败了。

class Original_1:
    def __init__(self):
        pass
    def meth1(self):
        pass
    def meth2(self):
        pass

class Original_2(Original_1):
    def __init__(self):
        Original_1.__init__(self)
    def meth3(self):
        pass

class Mixin:
    def __init__(self):
        pass
    def meth4(self):
        ...
        meth1(self)
        meth2(self)

class NewClass_1(Original_1, Mixin):
    def __init__(self):
        Original_1.__init__(self)
        Mixin.__init__(self)

class NewClass_2(Original_2, Mixin):
    def __init__(self):
        Original_2.__init__(self)
        Mixin.__init__(self)

现在的目标是用Mixin中的新方法扩展Original_1或Original_2,但是如果在mixin中使用meth1(),meth2()或meth3(),我会遇到一些问题。 1.我没有在mixin中引用Original_1或Origninal_2。 (这时它可以运行,但是我不喜欢它。)2.如果将Mixin设为Original_1的子代,它将中断。 我可以制作两个单独的NewClass_X,但随后我要复制所有这些代码。

Mixins用于通过使用多重继承为类添加功能(通常是方法)。

例如,假设您要使类的__str__方法返回大写形式的所有内容。 有两种方法可以执行此操作:

  1. 手动更改每个类的__str__方法:

     class SomeClass(SomeBase): def __str__(self): return super(SomeClass, self).__str__().upper() 
  2. 创建一个仅执行此操作并从中继承的mixin类:

     class UpperStrMixin(object): def __str__(self): return super(UpperStrMixin, self).__str__().upper() class SomeClass(SomeBase, UpperStrMixin): ... 

在第二个示例中,请注意UpperStrMixin作为独立类是如何完全没有用的。 它的唯一目的是与多重继承一起用作基类,并重写类的__str__方法。

在您的特定情况下,以下方法将起作用:

class Mixin:
    def __init__(self, option):
        ...

    def meth4(self):
        ...
        self.meth1()
        self.meth2()

class NewClass_1(Original_1, Mixin):
    def __init__(self, option):
        Original_1.__init__(self)
        Mixin.__init__(self, option)
        ...

class NewClass_2(Original_2, Mixin):
    def __init__(self, option):
        Original_2.__init__(self)
        Mixin.__init__(self, option)
        ...

即使Mixin.meth1Mixin.meth2 ,这也不是问题,因为从未直接创建Mixin的实例,并且仅通过多重继承间接使用了它。

由于Mixin是不是一个独立的类,你可以只写它的假设,必要的方法存在,而且它会发现他们对self假设self有问题提供,或从另一个类,它提供,派生meth1meth2

如果要确保这些方法存在,则可以在Mixin文档字符串中对其进行文档化,或者通过编程强制使用abc模块MixinABC并指定必须定义的方法。 如果给定的类(直接或通过继承)不提供它们,则尝试实例化它会收到错误消息(因为在定义这些方法之前,该类仍然是抽象的):

from abc import ABCMeta, abstractmethod

class Mixin(metaclass=ABCMeta):
    def __init__(self):
        pass

    @abstractmethod
    def meth1(self): pass

    @abstractmethod
    def meth2(self): pass

    def meth4(self):
        ...
        self.meth1()  # Method call on self will dispatch to other class's meth1 dynamically
        self.meth2()  # Method call on self will dispatch to other class's meth2 dynamically

除此之外,您可以通过适当地使用super显着简化代码,从而无需为每个父类显式调用__init__ 只要所有类都适当地使用super它们就会被自动调用(注意:为了安全起见,在这样的合作继承中,您通常会接受当前类的公认参数以及varargs,并传递您无法盲目地识别调用链的varargs ):

class Original_1:
    def __init__(self, orig1arg, *args, **kwargs):
        self.orig1val = orig1arg           # Use what you know
        super().__init__(*args, **kwargs)  # Pass what you don't

    def meth1(self):
        pass

    def meth2(self):
        pass

class Original_2(Original_1):
    def __init__(self, orig2arg, *args, **kwargs):
        self.orig2val = orig2arg                 # Use what you know
        super().__init__(self, *args, **kwargs)  # Pass what you don't

    def meth3(self):
        pass

class Mixin(metaclass=ABCMeta):
    # If Mixin, or any class in your hierarchy, doesn't need to do anything to
    # be initialized, just omit __init__ entirely, and the super from other
    # classes will skip over it entirely
    def __init__(self, mixinarg, *args, **kwargs):
        self.mixinval = mixinarg                 # Use what you know
        super().__init__(self, *args, **kwargs)  # Pass what you don't

    @abstractmethod
    def meth1(self): pass

    @abstractmethod
    def meth2(self): pass

    def meth4(self):
        ...
        self.meth1()  # Method call on self will dispatch to other class's meth1
        self.meth2()  # Method call on self will dispatch to other class's meth1

class NewClass_1(Original_1, Mixin):
    def __init__(self, newarg1, *args, **kwargs):
        self.newval1 = newarg1                   # Use what you know
        super().__init__(self, *args, **kwargs)  # Pass what you don't

class NewClass_2(Original_2, Mixin):
    def __init__(self, newarg2, *args, **kwargs):
        self.newval2 = newarg2                   # Use what you know
        super().__init__(self, *args, **kwargs)  # Pass what you don't

请注意,在各处使用super意味着您无需为父母显式调用每个__init__ 它会自动线性化调用,例如,在NewClass_2 ,单个super().__init__将委托给第一个父对象( Original_2 ),然后委托给第一个父对象( Original_2 ),后者又委托给Original_1 ,然后委托给Mixin (即使Original_1Mixin 一无所知 ) 。

在更复杂的多重继承中(例如,您通过两个都从其继承的不同父类从Mixin继承),使用super是合理地处理它的唯一方法。 super自然地线性化和消除了父类树的重复数据,因此即使有两个父类派生自它, Mixin.__init__仍将仅被调用一次,以防止细微的错误多次初始化Mixin

注意:您未指定要使用的Python版本。 在Python 3中,元类和super既更好又更简单,所以我使用了Python 3语法。 对于Python 2,您需要以不同的方式设置元类,并调用super提供当前类对象和显式的self ,这使其不太好用,但是Python 2在这一点上通常不太好,因此请考虑编写Python 3的新代码?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM