[英]Best way to forward/redirect methods/attributes in python class without redundant code/docstrings?
[英]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__
方法返回大写形式的所有内容。 有两种方法可以执行此操作:
手动更改每个类的__str__
方法:
class SomeClass(SomeBase): def __str__(self): return super(SomeClass, self).__str__().upper()
创建一个仅执行此操作并从中继承的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.meth1
和Mixin.meth2
,这也不是问题,因为从未直接创建Mixin
的实例,并且仅通过多重继承间接使用了它。
由于Mixin
是不是一个独立的类,你可以只写它的假设,必要的方法存在,而且它会发现他们对self
假设self
有问题提供,或从另一个类,它提供,派生meth1
和meth2
。
如果要确保这些方法存在,则可以在Mixin
文档字符串中对其进行文档化,或者通过编程强制使用abc
模块将Mixin
为ABC
并指定必须定义的方法。 如果给定的类(直接或通过继承)不提供它们,则尝试实例化它会收到错误消息(因为在定义这些方法之前,该类仍然是抽象的):
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_1
对Mixin
一无所知 ) 。
在更复杂的多重继承中(例如,您通过两个都从其继承的不同父类从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.