【问题标题】:in Python 3, can a superclass polymorphically call a subclass's constructor在 Python 3 中,超类可以多态地调用子类的构造函数吗
【发布时间】:2017-06-21 21:59:57
【问题描述】:

我想在一个类(Parent)中创建一个方法(比如copy),该方法将返回调用它的类或子类的对象。我想要type(x) == type(x.copy())

我尝试过的所有方法都不令人满意。

  • 使用超类构造函数返回超类(有道理,但我认为值得一试)。
  • 在超类使用的每个子类中创建一个函数init_me,但这违背了继承的目的。
  • 我开始探索 __new____init__,但很快决定 Python 一定有更好的方法。

示例代码

class Parent(object):
    def __init__(self, p1=p1_default, p2=p2_default, p3=p3_default):
        ...   # common stuff
        self._special_suff()
    def copy_works_if_subclass_does_extra(self):
        return self.init_me()
    def copy_only_does_superclass(self):
        return Parent()
    def copy_with_init(self):
        return self.__init__()
    def whoami(self):
        print('I am just a parent')

class Dad(Parent):
    def _special_stuff():
        ...     # Dad special stuff
        return 
    def whoami(self):
        print('I am a dad')
    def init_me(self):
        return Dad()

class Mom(Parent):
    def _special_stuff():
        ...     # Mom special stuff
        return 
    def whoami(self):
        print('I am a mom')

【问题讨论】:

    标签: python python-3.x inheritance constructor polymorphism


    【解决方案1】:

    如果我理解正确,您正在尝试在您的基类中编写一个copy 方法,当在派生类的实例上调用该方法时该方法仍然有效。这可以工作,但只有当您的子类只期望与基类相同的参数集时才容易。如果他们的 __init__ 方法需要不同的参数,您需要为每个派生类使用单独的 copy 方法。

    这里有一个简单的例子来说明它是如何工作的。诀窍是调用type(self) 以获取正确的类,然后使用适当的构造函数参数调用该类以获取新实例:

    class Base(object):
        def __init__(self, arg1, arg2, arg3):
            self.attr1 = arg1
            self.attr2 = arg2
            self.attr3 = arg3
    
        def copy(self):
            cls = type(self)
            return cls(self.attr1, self.attr2, self.attr3)
    
    class Derived(Base):
        def __init__(self, arg1, arg2, arg3):
            super().__init__(arg1, arg2, arg3)
            self.some_other_attr = "foo"
    

    在实践中这往往效果不佳,因为Derived 类通常需要一个额外的参数来设置它的额外属性。在这种情况下可能有效的一个选项是使用the copy module,而不是编写自己的copy 方法。函数copy.copy 将能够复制许多 Python 实例而无需任何特殊支持。

    【讨论】:

    • cls = type(self) 后跟 return cls(...) 回答了我的问题。我的印象是,如果我需要使用type,那么我需要重新考虑我的设计
    • 我不认为使用type 本质上是糟糕的设计。 一些依赖于它的坏代码(尤其是使用if type(foo) == Bar 而不是使用isinstance 的初学者代码),但是当你需要处理时使用type 是完全合理的类对象及其实例。它是 Python 的基本自省工具之一。
    【解决方案2】:

    很多,你把事情复杂化了。在子类上实现的简单构造函数的最小示例:

    import copy
    
    class Parent():
        def whoami(self):
            print('Just a parent')
        def __init__(self, name):
            self.name = name
        def copy(self):
            # Maybe copy.deepcopy instead
            return copy.copy(self)
    
    
    class Dad(Parent):
        def whoami(self):
            print('I am a dad')
        def __init__(self, name):
            super().__init__(name)
            self.gender = 'Male'
    

    如果你不需要,你甚至不需要 Python 中的构造函数。或者您可以在超类上设置一个,而在子类上不设置任何内容。

    一些用法:

    >>> dad = Dad("Clark Griswold")
    >>> dad.name
    'Clark Griswold'
    >>> dad.whoami()
    I am a dad
    >>> isinstance(dad, Dad)
    True
    >>> isinstance(dad, Parent)
    True
    >>> type(dad.copy()) == type(dad)
    True
    

    【讨论】:

    • 我正在尝试一种可能是死胡同的设计方法。超类构造函数有几个参数。这些参数对于所有继承的类都是相同的。所以这个想法是让子类实现一个“私有”方法来进行他们的专门构造。多态调度将处理子类中的变化。然后我实现了返回子类型所需的基本函数(不仅仅是示例中的copy)。 过于复杂?也许吧。但在更改设计之前,我想检查一下我的选择。
    • 您能否在原始帖子中包含一个专门构造的示例。我不确定我是否理解。
    • 我最后的评论是真实的,但具有误导性。主要挑战是copy 函数的重用。我希望Dad().copyMom.copy() 在超类中使用相同的代码,但在这两种情况下都返回子类。那么如何让超类的基础代码足够聪明,可以使用子类的构造函数呢?
    • 我在示例中添加了copy。不完全确定你的工作方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多