Python -继承的方法在重写__init__时中断



我有一个几何基类ExtrudedSurface和一个子类Cylinder,这是一种挤出表面。基类有一个通过构造其自身的修改版本来实现自身translate(不是就地)的方法。

我想通过子类Cylinder重用此方法,并让它返回一个新的,翻译的Cylinder。然而,正如现在实现的那样,这不起作用,因为Cylinder有一个不同的__init__签名,该签名在translate中被调用。

实现这一目标的最佳方法是什么?Cylinder如何使用继承方法translate并返回一个新的Cylinder对象?

编辑1:我认为这与LSP违反有关,但我不确定。

class ExtrudedSurface:
def __init__(self, profile: Curve, direction: Vector, length: float):
"""
An extruded surface geometry created by sweeping a 3D 'profile' curve along a 
'direction' for a given 'length'.
"""
self.profile = profile
self.direction = direction.normalize()
self.length = length
def translate(self, translation: Vector) -> ExtrudedSurface:
"""Return a translated version of itself."""
return self.__class__(
self.profile.translate(translation),
self.length,
self.direction
)

class Cylinder(ExtrudedSurface):
def __init__(self, point: Point, direction: Vector, radius: float, length: float):
"""Cylinder surface.
Args:
point: Center point of extruded circle.
direction: Direction vector of the cylinder (axis).
radius: Cylinder radius.
length: Extrusion length in 'direction'.
"""
direction = direction.normalize()
profile = Circle(point, direction, radius)
super().__init__(profile, direction, length)

长话短说:按惯例的方法是重写translate()方法,并从那里调用更新的构造函数。

现在,您可以重构您的类初始化,并将属性设置与其他所需的操作分开,然后创建一个新的类方法来克隆一个具有第一个实例的所有属性的实例,如果需要,只需调用此初始化。

如果不需要初始化,只使用"clone"方法是必需的。如果你碰巧调用了这个方法__copy__,那么你可以使用copy.copy来调用它,这几乎就像Python中的操作符一样,它本身就可以为你的最终用户提供价值。

此外——如果你的类除了设置普通属性外不需要初始化,而且根本不需要计算,那么copy.copy就会在你的实例中开箱工作——不需要额外的__copy__方法:

from copy import copy
class ExtrudedSurface:
def __init__(self, profile: Curve, direction: Vector, length: float):
"""
An extruded surface geometry created by sweeping a 3D 'profile' curve along a 
'direction' for a given 'length'.
"""
self.profile = profile
self.direction = direction.normalize()
self.length = length
def translate(self, translation: Vector) -> ExtrudedSurface:
"""Return a translated version of itself."""
new_profile = self.profile.translate(translation)
new_instance = copy(self)
new_instance.profile = new_profile
return new_instance


class Cylinder(ExtrudedSurface):
def __init__(self, point: Point, direction: Vector, radius: float, length: float):
...

只是尝试copy不会递归复制属性的事实,所以,如果self.direction向量是您正在使用的框架中的可变对象,这将使两个克隆绑定到相同的向量,如果它在原始中发生变化,则该变化将反映在克隆中。根据代码的性质,我假设那里的一切都是不可变的,所有转换都会创建新实例:那么这将起作用。否则,您还应该将原始.direction属性复制到新实例中。

In time:是的,这违反了LSP -但我一直认为,当涉及到实际问题时,LSP被给予了比它应有的更多的重视。

如果您的初始化更复杂,我所描述的更通用的代码将是:


class Base:
def __init__(self, base_args):
#code to set initial attributes
...
# call inner init with possible sideeffects:
self.inner_init()

def inner_init(self):
# code with side effects (like, attaching instance to a container)
# should be written in order to be repeat-proof - calls on the same instance have no effect
...
def __copy__(self):
new_instance = self.__class__.new()  
new_instance.__dict__.update(self.__dict__)
self.inner_init()

最新更新