【问题标题】:Base class methods to be used by all derived classes except one所有派生类都使用的基类方法,除了一个
【发布时间】:2016-07-04 18:32:20
【问题描述】:

我有一种情况,基类方法(Python 中的__init__ 方法)做了很多工作。派生类覆盖__init__ 以简单地设置一个对象变量,然后委托给基类__init__。这工作正常,但现在我有一个情况。

我有一些代码完全适合基类__init__,所有其他派生类除了一个(称之为A)都需要使用。想象一下,如果基类__init__ 是这样的。

def __init__(self):
   Action 1...
   Action 2...

现在,所有派生类都直接委托给这个类。但是,A__init__ 中应该只有操作 2。现在的位置很丑。我可以将动作 1 和动作 2 拆分为两个单独的函数,并让基类简单地调用 Action2。然后大多数类的初始化都是这样的。

def __init__(self):
   self.Action1()
   Other init code...
   super().__init__()

对于A 类,它将是

def __init__(self):
   Some init code....
   super().__init__()

但是你可以看到有一段代码(对self.Action1 的调用)我必须在大多数派生类中重复。这是不好的。我不确定如何用最少的重复代码优雅地编写代码,并且需要一些关于如何做到这一点的建议。

【问题讨论】:

  • 典型的答案是也许A真的不是子类。考虑将 A 作为父级(在层次结构中较高,而不是较低)
  • A 调用super().__init__() 后你能撤消 Action1 吗?
  • 类属性,用于描述要执行的操作和 __init__ 中的逻辑来执行它们 - 然后 A 会覆盖该属性?
  • @NoufalIbrahim,我知道 A 是个孩子。我是说也许它不应该是一个孩子,或者也许某些东西应该在你的父母之上,而 A 是从那里派生出来的。
  • 您是否考虑过使用合成解决方案?也许应该有一个对象可以做Action1,它可以是其他孩子的成员(不是A)。另一个动作是通过继承来完成的。

标签: python class oop inheritance


【解决方案1】:

最明确的解决方案是使用默认参数:

class Base:
    def __init__(self, *, doAction1=True):
       if doAction1:
           Action 1...
       Action 2...

那么在A课堂上你可以拥有:

def __init__(self):
   Some init code....
   super().__init__(doAction1=False)

【讨论】:

  • 这看起来不错。我做了类似的事情,但不是参数,而是设置基类所依赖的对象属性。
  • 除非出现其他建议,否则我会接受这个答案。这看起来是最简单的方法。
【解决方案2】:

可以被覆盖的类属性 - 类似于__init__ 中的默认参数。 (Python v2.7 类定义)。

class Foo(object):
   action1 = True
   action2 = True
   def __init__(self):
      if self.action1:
         print('action1')
      if self.action2:
         print('action2')

class Bar(Foo):
   def __init__(self):
      super(Bar, self).__init__()
      print('Bar')

class Baz(Foo):
   action2 = False
   def __init__(self):
      super(Baz, self).__init__()
      print('Baz')

【讨论】:

  • 这可行,但我认为@ecatmur 的解决方案更清洁。
  • @NoufalIbrahim ... 我赞成 ecatmur 的解决方案,然后决定将其发布为 对比度
【解决方案3】:

在做出设计决策时,通常值得考虑继承是否是正确的方法。深入思考你是否真的需要继承,或者它是否只会让事情变得复杂。

如果您决定实际上不需要继承(您所询问的当前问题可能(或可能不是)表明情况确实如此),在诉诸继承之前,我建议您探索使用以下方法解决您的问题像这样:

def Action1(obj):
    <do Action 1>

def Action2(obj):
    <do Action 2>

def Action1and2(obj):
    Action1(obj)
    Action2(obj)

class A:
    def __init__(self):
        Action2(self)

class Other:
    def __init__(self):
        Action1and2(self)

请注意,对于 Other 对象,您仍然只有 1 行代码,并且您完全避免了继承。

如果您决定需要继承 - 即,您创建的父对象不仅仅是通过一些操作进行设置(它们提供额外的功能或对数据的访问) - 您可以尝试使用以下方法解决您的问题取而代之的是,像这样:

class Action1Doer:
    def __init__(self,obj):
        <do Action 1>

class Action2Doer:
    def __init__(self,obj):
        <do Action 2>

class Action1and2Doer:
    def __init__(self,obj):
        self.a1 = Action1Doer(obj)
        self.a2 = Action2Doer(obj)

class A:
    def __init__(self):
        self.thing2 = Action2Doer(self)

class Other:
    def __init__(self):
        self.thing1and2 = Action1and2Doer(self)

请再次注意,每个初始化方法中只有一行代码。顺便说一句,在编码世界中以这种方式处理事情iscertainlynothingnew

你也可以把东西混在一起:

def Action1(obj):
    <do Action 1>

class Action2Doer:
    def __init__(self,obj):
        <do Action 2>

class Action1and2Doer:
    def __init__(self,obj):
        Action1(obj)
        self.a2 = Action2Doer(obj)

class A:
    def __init__(self):
        self.thing2 = Action2Doer(self)

class Other:
    def __init__(self):
        self.thing2 = Action1and2Doer(self).a2

【讨论】:

  • 我想这是可能的,但“Action1and2Doer”听起来相当做作。没有?
  • 这就是您已经在使用的基类,对吧?我只是给它一个描述性的名称以使解释清楚。
猜你喜欢
  • 2014-10-29
  • 2017-06-12
  • 2018-10-12
  • 2019-07-25
  • 1970-01-01
  • 1970-01-01
  • 2020-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多