【问题标题】:How to correctly inherit from class that returns other class based on condition?如何正确继承根据条件返回其他类的类?
【发布时间】:2014-01-11 20:05:52
【问题描述】:

我将首先编写代码,然后详细解释我想要实现的目标,因为这样更容易:

global child_selector

class Base(object): 
  def __init__(self):
    self.extended_name = self.name + '_Base'


class Child1(Base): 
  def __init__(self):
    super(Child1, self).__init__()

  def print_my_name(self):
    print 'I am ChildOne'

class Child2(Base):

  def __init__(self):
    super(Child2, self).__init__()

  def print_my_name(self):
    print 'I am ChildTwo'

class ChildAsBaseSelector(object):
  def __new__(cls):
    if child_selector == 1:
      return Child1()
    elif child_selector == 2:
      return Child2()
    else:
      print 'No child selected'

现在,如果我创建一个继承自 Child1 或 Child2 并将 self.name 作为实例变量的类,一切都会按预期工作:

class Testing1(Child1):

  def __init__(self):
    self.name = 'Testing1'
    super(Testing1, self).__init__()

a = Testing1()
a.print_my_name() # 'I am ChlidOne'
print a.extended_name # 'Testing1_Base'

但是,如果我创建另一个将直接从 ChildAsBaseSelector() 继承的类,事情就会变得可疑:

class Testing2(ChildAsBaseSelector):

  def __init__(self):
    self.name = 'Testing2'
    super(Testing2, self).__init__()

child_selector = 1
a = Testing2() 
# Will raise AttributeError: 'Child1' object has no attribute 'name'

在阅读了 '__new__()' 的 python 文档后,我认为问题在于:'如果 __new__() 没有返回 cls 的实例,则不会调用新实例的 __init__() 方法。这并没有真正的帮助,因为我不知道如何“修复它”。

我也知道我可以这样做:

if child_selector == 1:
  ChildAsBaseSelector = Child1
if child_selector == 2:
  ChildAsBaseSelector = Child2

并且比直接从 ChildAsBaseSelector 继承。但我发现这在某种程度上并不优雅,并将 python 的灵活性推向了极限。 我真正想做的是有一个类,它会根据条件返回另一个类,然后我可以使用它来继承。有什么提示吗?我想元类(一个我正在努力理解的概念)现在会派上用场。

感谢任何提示。谢谢!

【问题讨论】:

    标签: python class inheritance metaclass


    【解决方案1】:

    你为什么不让 ChildAsBaseSelectorBase 继承?在您的示例中,super(Testing2, self).__init__() 将解析为object.__init__()(因此它不会按照您的预期进行),但是-正如您所提到的-它无论如何都不会被调用,因为它不在__mro__ 和@987654326 中@也不是Child2

    无论如何:让您的Base.__init__() 依赖于子类设置属性之前 调用super().__init__() 是不好的设计 - 如果Base 需要它,那么它应该作为参数传递或Base 本身有一个可用的默认值。

    此外,如果ChildAsBaseSelector 的唯一存在理由是基于全局/设置/任何内容创建Child1Child1 的实例,则只需使用工厂函数...没有“新” ' 关键字真的是一种祝福。

    【讨论】:

    • '为什么不让 ChildAsBaseSelector 从 Base 继承?'。因为 Base 适用于常用方法,而 Child1 和 Child2 具有我覆盖的特定方法(例如 print_my_name)。从 Base 继承意味着只继承公地。关于糟糕的设计部分 - 有许多类将从 Child1 或 Child2 继承。它们都有一个 self.name 变量,用于在 Base 中创建 self.extended_name(实际上是一个配置文件选项)。因此将 self.name 设置为虚拟值将引发 Config 文件没有选项错误
    • 如果ChildAsBaseSelector 只是为了实例化其他子类,那么你不在乎它是否“只继承公共资源”,因为你永远不会实例化它。 wrt/name 属性,如果它对所有对象层次结构都是通用的,那么它应该作为必需参数传递给Base.__init__,这将 1/ 生成更明确的代码并 2/ 避免您的 AttributeError。现在我没有所有的上下文,所以我只能评论你发布的内容;)
    • 大局是我正在尝试重构大量代码。扩展问题以完全涵盖我试图解决的所有问题会使这篇文章过于冗长(并且与问题本身无关)。简而言之,我不想使用“名称”作为必需参数,因为还有其他类继承自 Testing1、Testing2 ......也需要使用它们继承自的类设置的“名称”(即 Child1 或孩子2)。将其作为必需参数传递意味着每次都将其覆盖。感谢您花时间回答我的问题。
    猜你喜欢
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 2011-03-01
    • 2020-11-13
    • 2021-02-08
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多