在Python中,我想直接从Parent类的实例构造一个Child类的实例。例如:
A = Parent(x, y, z)
B = Child(A)
这是我认为可行的黑客攻击:
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)
似乎hack的工作正如我预期的那样,但编译器最后会抛出一个TypeError。我想知道我是否可以重载TypeError使其忽略B = Child(A)习语,但我不知道该怎么做。无论如何,您能否告诉我您从实例继承的解决方案?
谢谢!
答案 0 :(得分:7)
类__new__
中的Child
返回Child
的实例后,将在该实例上调用Child.__init__
(使用相同的参数__new__
) - 显然它只是继承Parent.__init__
,只用一个arg(另一个Parent
,A
)来调用就不好了。
如果没有其他方式可以制作Child
,您可以定义Child.__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
答案 1 :(得分:5)
好的,所以我没有意识到你对我的解决方案的静态副本感到高兴,直到我已经完成了我的解决方案。但我决定不浪费它,所以无论如何它在这里。与其他解决方案的不同之处在于,即使更新了它,它实际上也会从父级获取属性。
_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包。事实上,在某些情况下,这是一种血腥的需求解决方案。
答案 2 :(得分: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
答案 3 :(得分:2)
我知道这是一个非常古老的线索,但我最近遇到了与亚历山德拉相同的挑战,这是我能找到的最相关的话题。我有一个具有许多属性的父类,我希望通过保留所有方法和属性,添加一些,以及修改/覆盖其他属性来实际“修补”它的实例。一个简单的子类不起作用,因为属性将在运行时由用户填充,我不能只从父类继承默认值。经过大量的修修补补后,我发现使用__new__
非常干净(虽然相当hacky)。这是一个例子:
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的构造函数中更改Parent类的__class__
属性。因此,将像往常一样调用Child的__init__
方法,据我所知,Child类应该与任何其他继承类完全相同。
答案 4 :(得分:2)
我发现这(封装)是最干净的方式:
class Child(object):
def __init__(self):
self.obj = Parent()
def __getattr__(self, attr):
return getattr(self.obj, attr)
通过这种方式,您可以使用所有Parent方法和您自己的方法,而不会遇到继承问题。
答案 5 :(得分:1)
我知道这是一个老话题,但是我希望未来的读者仍然可以从我找到的简单解决方案中受益。
获取实例的 init 值的一种非常简单的方法是使用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
答案 6 :(得分:0)
谢谢,伙计们,这很快!我首先阅读了Alex的评论,并将Child的__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建议的非常相似(正如我刚刚发现的那样)。它有效。只有他和他们的行
if len(args) == 1 and isinstance(args[0], Parent):
比我的更干净。
再次感谢!!
答案 7 :(得分: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