15

在 Python 中,我想直接从 Parent 类的实例构造 Child 类的实例。例如:

A = Parent(x, y, z)
B = Child(A)

这是一个我认为可能有用的hack:

class Parent(object):

    def __init__(self, x, y, z):
        print "INITILIZING PARENT"
        self.x = x
        self.y = y
        self.z = z

class Child(Parent):

    def __new__(cls, *args, **kwds):
        print "NEW'ING CHILD"
        if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>":
            new_args = []
            new_args.extend([args[0].x, args[0].y, args[0].z])
            print "HIJACKING"
            return Child(*new_args)
        print "RETURNING FROM NEW IN CHILD"
        return object.__new__(cls, *args, **kwds)

但是当我跑步时

B = Child(A) 

我得到:

NEW'ING CHILD  
HIJACKING  
NEW'ING CHILD  
RETURNING FROM NEW IN CHILD  
INITILIZING PARENT  
Traceback (most recent call last):  
  File "classes.py", line 52, in <module>  
    B = Child(A)  
TypeError: __init__() takes exactly 4 arguments (2 given) 

看起来黑客就像我预期的那样工作,但编译器最后会抛出一个 TypeError 。我想知道是否可以重载 TypeError 以使其忽略 B = Child(A) 习语,但我不知道该怎么做。无论如何,请您给我您从实例继承的解决方案吗?

谢谢!

4

8 回答 8

8

我发现这(封装)是最干净的方式:

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

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

这样您就可以使用所有 Parent 的方法和您自己的方法,而不会遇到继承问题。

于 2013-01-06T13:22:56.430 回答
8

一旦__new__在类中Child返回 , 的实例ChildChild.__init__将在该实例上调用(使用相同的参数__new__)-显然它只是继承Parent.__init__,这并不适合仅使用一个 arg (另一个Parent, A)调用。

如果没有其他方法Child可以制作 a,您可以定义 aChild.__init__接受一个 arg(它忽略)或三个(在这种情况下它调用Parent.__init__)。但是放弃__new__并拥有所有逻辑更简单Child.__init__,只需适当地调用Parent.__init__

用一个代码示例来具体说明:

class Parent(object):

    def __init__(self, x, y, z):
        print "INITIALIZING PARENT"
        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return "%s(%r, %r, %r)" % (self.__class__.__name__,
            self.x, self.y, self.z)


class Child(Parent):

    _sentinel = object()

    def __init__(self, x, y=_sentinel, z=_sentinel):
        print "INITIALIZING CHILD"
        if y is self._sentinel and z is self._sentinel:
            print "HIJACKING"
            z = x.z; y = x.y; x = x.x
        Parent.__init__(self, x, y, z)
        print "CHILD IS DONE!"

p0 = Parent(1, 2, 3)
print p0
c1 = Child(p0)
print c1
c2 = Child(4, 5, 6)
print c2
于 2009-07-04T01:11:01.553 回答
8

好的,所以直到我的解决方案已经完成一半时,我才意识到您对参数的静态副本感到满意。但我决定不浪费它,所以无论如何它都在这里。与其他解决方案的不同之处在于,即使它们已更新,它实际上也会从父级获取属性。

_marker = object()

class Parent(object):

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

class Child(Parent):

    _inherited = ['x', 'y', 'z']

    def __init__(self, parent):
        self._parent = parent
        self.a = "not got from dad"

    def __getattr__(self, name, default=_marker):
        if name in self._inherited:
            # Get it from papa:
            try:
                return getattr(self._parent, name)
            except AttributeError:
                if default is _marker:
                    raise
                return default

        if name not in self.__dict__:
            raise AttributeError(name)
        return self.__dict__[name]

现在如果我们这样做:

>>> A = Parent('gotten', 'from', 'dad')
>>> B = Child(A)
>>> print "a, b and c is", B.x, B.y, B.z
a, b and c is gotten from dad

>>> print "But x is", B.a
But x is not got from dad

>>> A.x = "updated!"
>>> print "And the child also gets", B.x
And the child also gets updated!

>>> print B.doesnotexist
Traceback (most recent call last):
  File "acq.py", line 44, in <module>
    print B.doesnotexist
  File "acq.py", line 32, in __getattr__
    raise AttributeError(name)
AttributeError: doesnotexist

要获得更通用的版本,请查看http://pypi.python.org/pypi/Acquisition包。事实上,在某些情况下,这是一个非常需要的解决方案。

于 2009-07-04T10:12:09.457 回答
4

您没有为 Child 定义构造函数 (init),因此调用 Parent 构造函数,需要 4 个参数,而仅传入 2 个参数(从 new 传入)。这是实现您想要的一种方法:

class Child(Parent):
    def __init__(self, *args, **kwargs):
        if len(args) == 1 and isinstance(args[0], Parent):
            Parent.__init__(self, args[0].x, args[0].y, args[0].z)

        else:
            # do something else
于 2009-07-04T01:18:16.950 回答
2

我知道这是一个非常古老的话题,但我最近遇到了和 Alexandra 一样的挑战,这是我能找到的最相关的话题。我有一个具有许多属性的父类,我想通过保留其所有方法和属性、添加一些方法和属性以及修改/覆盖其他属性来“修补”它的一个实例。一个简单的子类是行不通的,因为属性将在运行时由用户填写,我不能只从父类继承默认值。经过大量修补后,我找到了一种非常干净(尽管相当老套)的方法,即使用__new__. 这是一个例子:

class Parent(object):
    def __init__(self):
        # whatever you want here
        self.x = 42
        self.y = 5
    def f(self):
        print "Parent class, x,y =", self.x, self.y

class Child(Parent):
    def __new__(cls, parentInst):
        parentInst.__class__ = Child
        return parentInst
    def __init__(self, parentInst):
        # You don't call the Parent's init method here
        self.y = 10
    def f(self):
        print "Child class, x,y =", self.x, self.y

c = Parent()
c.f()  # Parent class, x,y = 42 5
c.x = 13
c.f()  # Parent class, x,y = 13 5
c = Child(c)
c.f()  # Child class, x,y = 13 10

唯一特殊的部分是在 Child 的构造函数中更改__class__Parent 类的属性。因此,__init__将照常调用 Child 的方法,据我所知,Child 类的功能应该与任何其他继承的类完全一样。

于 2012-08-15T18:09:21.210 回答
1

谢谢各位,太快了!我首先阅读了亚历克斯的评论,然后将孩子的评论改写__init__

def __init__(self, *args, **kwds):
    if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>":
        new_args = [args[0].x, args[0].y, args[0].z]
        super(Child, self).__init__(*new_args, **kwds)
    else:
        super(Child, self).__init__(*args, **kwds)

这与 abhinavg 的建议非常相似(正如我刚刚发现的那样)。它有效。只有他和ars的线

if len(args) == 1 and isinstance(args[0], Parent):

比我的干净。

再次感谢!!

于 2009-07-04T01:37:25.977 回答
1

我知道这是一个旧线程,但我希望未来的读者仍然会从我找到的简单解决方案中受益。

获取实例的初始值的一种非常简单的方法是使用 vars() 函数。它返回对象的dict属性(返回类/实例属性)。我发现这种方法比使用new更具可读性,并且它可以处理任意数量的属性。

class Parent(object): 
    def __init__(self, x, y, z, **kwargs):
        # kwargs is not necessary but
        # it handles if too many variables are given
        print("Accessing Parent")
        self.x = x
        self.y = y
        self.z = z

class Child(Parent):
    def __init__(self, instance):
        print("Child")
        instance_attrs = vars(instance)
        super().__init__(**instance_attrs)
        # Parent.__init__(self, **instance_attrs)
A = Parent(x, y, z)
# Accessing Parent
print(vars(A))
# {"x": x, "y": y, "z": z}
B = Child(A)
# Child
# Accessing Parent
print(A.x, B.x) 
# x x
于 2020-07-15T20:54:11.223 回答
0

我希望不要偏离主题,因为我很具体,但这是我发现的唯一相关问题。

如果您想通过组合继承来自父对象的属性,并且不复制任何值,您可以这样做:

class Agro:
    def __init__(self, parent, prop1=None, prop2=None):
        self.parent = parent
        self._prop1 = prop1
        self._prop2 = prop2

    @property
    def prop1(self):
        try:
            if self._prop1 is None:
                return self.parent.prop1
            else:
                return self._prop1
        except AttributeError as e:
            return None

    @property
    def prop2(self):
        try:
            if self._prop2 is None:
                return self.parent.prop2
            else:
                return self._prop2
        except AttributeError as e:
            return None

然后你实例化:

ag = Agro(None, prop1="nada")  # prop1: nada, prop2: None
ag2 = Agro(ag, prop1="nada2")  # prop1: nada2, prop2: None
ag3 = Agro(ag2, prop2="youhou")  # prop1: nada2, prop2: youhou
ag4 = Agro(ag3)  # prop1: nada2, prop2: youhou

有趣的是,当您查看实例中实际保存的内容时,__dict__属性仅保存一次。因此,您可以动态修改属性值:

ag3._prop2 = "yaya"
print(ag4.prop2)  # yaya
于 2018-10-24T13:35:09.827 回答