【问题标题】:How does one pass a constructor method to another method and invoke it there?如何将构造函数方法传递给另一个方法并在那里调用它?
【发布时间】:2012-07-21 18:05:51
【问题描述】:

我试图避免在两个类的克隆方法中重复相同的代码,一个继承自另一个。我目前的方法是将两个类共有的代码放在基类的(非静态)克隆方法中,并从扩展类中调用它。为此,我试图将扩展类的构造函数传递给基类,以便可以在那里调用它。这是我目前正在做的一个简化示例:

class S(object):
    """Base class"""
    def __init__(self):
        self.a = 0

    def clone(self, constructor=None):
        if constructor is None:
            constructor = self.__init__
        cloned = constructor()
        # Expecting: cloned.a = 1, cloned.b = 7

        assert cloned is not None  # raises AssertionError
        cloned.a = self.a  # Set a to 2
        return cloned

class C(S):
    """Child class, extending Base"""
    def __init__(self):
        self.a = 1
        self.b = 7

    def clone(self):
        cloned = super(C, self).clone(self.__init__)
        # Expecting: cloned.a = 2, cloned.b = 7

        cloned.b = self.b  # Set b to 8
        return cloned

if __name__ == '__main__':
    c1 = C()
    c1.a = 2
    c1.a = 8
    c2 = c1.clone()

此时我的问题是:

  • 为什么调用基类的clone方法会返回None

  • 我是否必须将方法绑定到一个类才能使用它,使用 types.MethodType__get__?

  • 您认为更好的方法是什么?

【问题讨论】:

  • 您知道copy 模块吗?
  • @KarlKnechtel:是的,但我做了一些错误的假设,让我相信这对我想做的事情来说还不够好。原来是这样。

标签: python inheritance methods


【解决方案1】:

虽然__init__() 在 Python 中通常被称为构造函数,但实际上并非如此。它获取作为self 参数传入的实例,并返回None。该实例是由__new__() 方法较早构造的,该方法是实际的构造函数。不过,您不应该传入对__new__ 的引用。传入类型本身更容易、更直接。

编辑:实现clone 方法的正确方法是另一种方法:坚持通常的Python 协议,并在您的实例上实现__getstate__()__setstate__()。然后您可以使用copy 模块中的函数正确克隆您的实例。

【讨论】:

  • 在其他语言中,构造函数也在已经分配的内存和构造的上下文中运行,但不是初始化的对象。 __init__ 不是构造函数到底有什么不同? O_o
  • @BartoszKP 这个问题的 OP 期望构造函数生成一个新对象并返回它,但它没有,我回答说清楚了。也就是说,__init__() 的角色和 C++ 中的构造函数肯定存在差异。最重要的区别是__new__() 已经返回了一个处于一致状态的完全工作的对象,在某些情况下甚至是一个初始化的对象(例如对于不可变类型)。 __new__() 比 C++ 中的分配器做得更多,这就是为什么我在这里称它为“实际”构造函数。
  • 你所说的“完全工作”是什么意思?对象是否“完全工作”取决于__init__ 中的代码和基类,它可以执行与 C++ 中的 vtable 完全相同类型的设置(您可能已经想到了)。例如,它应该从子类中显式调用 base 的 __init__,所以在这种情况下我不会称它为“完全工作”。
  • @BartoszKP 我不会坚持这个术语——调用__init__() 构造函数和__new__() 构造函数都是合理的。这个答案中的措辞可能太强了,但我并不在乎——我主要是为了回答 OP 的问题。
  • 你是对的,我承认这是一个区别(不大,但值得一提)。而且,这正是文档所说的(这两者一起作为构造函数工作):) 干杯!
【解决方案2】:

您的基类克隆方法是多余的。从任何上下文调用 self.__init__() 将遵循对象的特定实例的 MRO,这意味着在 C 的实例上调用 clone() 将从 S 中的代码调用 C.__init__() 而不指定使用 @987654326明确地@。

如果您想做的是能够自定义 clone() 而不必创建一个全新的类,或者根据其他因素具有不同的行为,我建议您查看工厂/构建器模式。

另外,特别是对于clone() 类型的操作,您可能希望查看重载__copy__()__getstate__()__setstate__() 并使用copy.copy()copy.deepcopy(),而不是实现一个新的辩证元素。

【讨论】:

    猜你喜欢
    • 2011-11-27
    • 2014-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多