【问题标题】:Python3 generic inheritance hierarchyPython3 通用继承层次结构
【发布时间】:2020-10-24 06:05:17
【问题描述】:

这个问题是针对 python 3 的。假设我有一个像这样的类层次结构

class Base():
   def calculate():
       return 0

class Derived1(Base):
     def calculate():
        # some calculation

class Derived2(Base):
     def calculate():
        # some calculation

现在,我要做的是创建一个类,它定义了从派生类继承的通用方式,然后覆盖计算。换句话说,本着 C++ 模板的精神,避免复制子类代码,而是指定一种通用的子类化方式,然后能够将子类定义为一个衬垫,如下所示:

# pseudocode
class GenericDerived5(someGenericBase):
      def calculate():
          return super().calculate() + 5

class GenericDerived6(someGenericBase):
      def calculate():
          return super().calculate() + 5

class Derived5_1 = GenericDerived5(Derived1)
class Derived6_1 = GenericDerived6(Derived2)

(计算不是字面上这样,只是说明了继承结构的组合性质) 这段代码会是什么样子,我需要 python3 的相关工具是什么?我听说过元类,但不是很熟悉。

【问题讨论】:

  • 你能澄清你想要做什么吗?您显然已经知道类定义和继承,为什么不使用此工具解决问题? class Derived5_1(GenericDerived5, Derived1) 应该做你想做的事,并且几乎是逐字逐句的。
  • 我在问题中所说的:“从派生类继承的通用方法”。与其明确指定基类和派生类的所有组合,不如定义一种通用的继承方式,可能使用类型,然后将类定义为单行。
  • 我不了解您的要求如何与 mixins 冲突。多重继承也是一行代码。如前所述,它与伪代码几乎一字不差。您能否澄清一下您还需要什么,或者 mixins 与您想要的具体有什么冲突?

标签: python python-3.x inheritance generic-programming metaclass


【解决方案1】:

粗略地说,混合类是一个不打算直接实例化或充当独立基类的类(除了其他更专业的混合类),而是提供一个小的另一个类可以继承的功能子集。

在这种情况下,您的 GenericDerived 类是混合的完美示例:您没有创建 GenericDerived 的实例,但您可以从它们继承以将 calculate 方法添加到您自己的类中。

class Calculator:
    def calculate(self):
        return 9

class Calculator1(Calculator):
      def calculate(self):
          return super().calculate() + 5

class Calculator2(Calculator):
      def calculate(self):
          return super().calculate() + 10

class Base(Calculator):
    ...

请注意,BaseCalculator 层次结构彼此独立。 Base 除了提供其他功能之外,还提供基本的calculate 功能。 Base 的子类可以使用从Base 继承的calculate(通过Calculator),也可以从Calculator 的子类继承。

class Derived1(Base):
    ...

class Derived2(Base, Calculator1):
    ...

class Derived3(Base, Calculator2):
    ...

【讨论】:

  • 我知道mixin。这不是我想要的,与问题无关。这更符合 C++ 模板的精神。将澄清问题。
【解决方案2】:

工厂函数体内的类定义

去那里最直接的方法是真的直接 - 但会感觉有点尴尬:

def derived_5_factory(Base):
    class GenericDerived5(Base):
        def calculate(self):
            return super().calculate() + 5
    return GenericDerived5

def derived_6_factory(Base):
    class GenericDerived6(Base):
        def calculate(self):
            return super().calculate() + 6
    return GenericDerived6

Derived5_1 = derived_5_factory(Derived1)
Derived6_2 = derived_6_factory(Derived2)

不方便的部分是您的类需要通用基础 必须在函数体内定义。这样,Python 重新执行 class 语句本身,具有不同的 Base,利用 在 Python 中的类是第一类对象。

这段代码的不便之处在于 (1) 类主体必须在函数内部,以及 (2) 它可能是完全错误的方法:

多重继承

如果您可以有一个额外的继承级别 - 这是您的示例的唯一区别,这是“正确”的方法。实际上,除了在其继承链中显式地包含以前的“GenericDerived”类之外,它们的行为将完全符合预期:


class Base():
   def calculate():
       return 0

class Derived1(Base):
     def calculate(self):
        return 1

class Derived2(Base):
     def calculate(self):
        return 2

#  mix-in bases:

class MixinDerived5(Base):
    def calculate(self):
        return super().calculate() + 5

class MixinDerived6(Base):
    def calculate(self):
        return super().calculate() + 6


Derived5_1 = type("Derived5_1", (MixinDerived5, Derived1), {})
Derived6_2 = type("Derived6_2", (MixinDerived6, Derived2), {})

这里,不是使用class 语句,而是使用type 调用创建一个动态类,同时使用需要动态基础的类和动态基础作为其bases 参数。就是这样 - Derived5_1 是一个完整的 Python 类,其继承链中包含两个 Bases

请注意,Python 的super() 将完全按照常识所期望的那样,在到达“Base”之前通过额外的中间“派生”类“重新路由”自身。所以,这是我在粘贴上面的代码后在交互式控制台上得到的:

In [6]: Derived5_1().calculate()                                                                 
Out[6]: 6

In [7]: Derived6_2().calculate()                                                                 
Out[7]: 8

【讨论】:

  • 我不确定是否值得将明确调用 type 拖入答案; class Derived5_1(MixinDerived5, Derived1): ... 也可以。
  • 工厂函数方法的问题在于,您实际上是在每次调用它时创建单独的 GenericDerived* 类,这意味着两个类看起来来自同班真的没有。
  • 虽然type 通常做正确的事,types.new_class always does
  • Type 似乎符合问题的精神,因为我正在寻找类似于 C++ 模板的东西,在某种程度上。将进一步研究这一点。请重新阅读问题,在那里添加一些细节。
  • 工厂函数应该增加缓存机制,避免每次调用都重新创建类。但是,如果您真的想要 ONE 类的行为就像它从不同的基础派生一样,具体取决于它的调用方式 - 这很棘手 - 它可以通过一些应该用来代替 super() 的机制来完成,并且可以放入在元类上。
猜你喜欢
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多