用父对象实例化子对象

3

我有一个函数,它返回类 Parent 的实例:

def generateParent():
   do_stuff
   return Parent(some_parameters)

现在我想用调用generateParent()的结果来初始化一个Parent的子类:
class Child(Parent):
    def __new__():
        return generateParent(some_other_parameters) 

问题是,当我在Child类中覆盖了一些来自Parent的方法,然后在程序的Child实例中调用它们时,原始的Parent方法被调用,而不是来自Child的新方法。我这里做错了什么吗?我在这里使用了正确的设计吗?

编辑:我既没有访问Parent也没有访问generateParent()

解决方案(感谢@Paul McGuire的回答):

class Child(object):
    def __init__(self):
        self.obj = generateParent()

    def __getattr__(self, attr):
        return getattr(self.obj, attr)

4
你故意没有从构造函数返回一个子对象,而是返回了一个父对象,然后你感到惊讶它表现得像个父对象? - Gareth Latty
我明白了,那么我该如何才能扩展一个类,并用由函数返回的父类实例进行初始化? - elyase
3个回答

5

由于generateParent不是您的代码,因此您可能希望使用包含和委托而不是继承。也就是说,不要定义一个子类,而是定义一个包装类来包含生成的对象,并在需要时将方法调用转发到它,但可以在包装器中添加新行为或更改行为。

这个问题中,OP遇到了类似的情况,需要在库中生成一个类,但想要扩展该类和/或修改该类的某些行为。看看我在那个问题中如何添加包装器类,您可能考虑在这里做类似的事情。


我尝试了你的解决方案,但对我完全不起作用。我尝试调用Child对象的任何方法,但总是调用Parent方法。 - elyase
1
这个解决方案是你最好的选择。如果你在使用它时遇到了困难,请发布代码的详细信息。 - Ned Batchelder
好的,这是最好的解决方案,我在测试代码时出现了错误。 - elyase

1

以下是一种实现方法:

def generateChild(params):
    p = generateParent(params)
    p.__class__ = Child
    return p

class Child(Parent):
    # put method overrides etc here

childinstance = generateChild(some_params)

使用这个,我该如何创建和使用子对象?我是否总是需要在之前调用generateChild()? - elyase
你可以把这两行代码 (p = generateParent(params); p.__class__ = Child)放到 Child 类的 __new__ 方法中。 - David Robinson
@DavidRobinson 我觉得你的建议更符合 Python 的风格,因为我可以通过 new_child = Child() 来创建对象。 - elyase
我自己也用过这种技术,但你真的需要小心。请注意,在这种情况下 Child.__init__永远不会被调用,并且在 Child.__init__ 中定义的任何实例变量都不会被创建,这可能会导致依赖它们的 Child 方法出现问题。如果您这样做,请编写 Child.__init__ 以调用像 Child.addChildAttributes() 这样的方法,该方法将添加任何特殊属性,然后在 generateChild() 中,在设置 p.__class__ 后,再调用 p.addChildAttributes() - PaulMcG
是的,我在使用这个解决方案时遇到了错误,因为__init__(来自父类?)被调用并带有我的初始化参数,而我没有定义(或需要定义)任何__init__。 - elyase

1
也许你想让generateParent能够创建其他类的实例:
def generateParent(cls=Parent):
    do_stuff
    return cls(some_parameters)

现在这将创建一个Child对象:
child = generateParent(Child)

或者你想让Parent及其所有派生类使用共同的初始化代码?
class Parent(object):
    def __init__(self):
        do_stuff
        # init from some_parameters

class Child(Parent):
    # blah..

使你的Child对象能够从已创建的Parent对象中复制信息:
class Child(Parent):
    def __init__(self):
        model_parent = generateParent()
        self.a = model_parent.a
        self.b = model_parent.b
        # etc.

有趣的方法,不幸的是,正如我在现在已删除的答案中所评论的那样,我无法访问generateParent函数。 - elyase
也许更新问题以包含这个重要的信息? - Ned Batchelder
抱歉,我已经添加了这些信息。 - elyase
关于1和2,我既无法访问generateParent()也无法访问Parent,而且3中,我需要从modelParent中复制很多属性。 - elyase
@elyase:你能提供一下你希望Child和Parent有什么不同的细节吗?虽然还有其他可能性,但如果没有具体细节,很难推荐特定的方案。 - Ned Batchelder
实际上,我只想重载(编写自己的)Parent类中的__getitem__方法。 - elyase

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接