麻烦的多继承代码

时间:2013-04-26 15:08:15

标签: python class inheritance python-3.x multiple-inheritance

我正在尝试在python中创建一个简单的Rectangle - 类,但我还需要在代码中使用点和大小,所以我试图从{{继承Rectangle 1}}和Point。 问题是,我的Size初始化方法看起来很糟糕,我不确定它是否适合任何代码。 这就是我得到的:

Rectangle

然而,它看起来很糟糕,甚至不起作用:class Size: def __init__(self, width=0, height=0): self.width = width self.height = height class Point: def __init__(self, x=0, y=0): self.x = x self.y = y class Rectangle(Size, Point): def __init__(self, size=None, position=None): #I'd rather not use 4 variables if size: super(Size, self).__init__(size.width, size.height) else: super(Size, self).__init__() if position: super(Point, self).__init__(position.x, position.y) else: super(Point, self).__init__()

有更清洁的方法吗? 我当然可以强迫我的矩形取TypeError: object.__init__() takes no parameterssize(不是让它们可选)但我宁愿不这样做。 我还可以将position定义为与Rectanglehas-a而不是Size关系建立Point关系,但这甚至不是正确的OOP而我是主要是在这里学习,所以我宁愿不这样做。

3 个答案:

答案 0 :(得分:3)

您使用super()错误。您在调用中指定您自己的类,以转到下一个父类。你所有的其他课程都需要做同样的事情。你现在这样做的方式,你要说的是Size的父类,object

这是Python 3,你可以在没有任何参数的情况下进行super(),它会为你解决所有问题。

当然,这意味着您无法使用super()显式调用两个父类,因为给定的类只有一个super()。如果您真的想这样做,则必须将其称为Size.__init__(self, ...),依此类推。

但我不得不说,你已经制定的继承计划没有任何意义。矩形既不是一种尺寸也不是一种点。相反,它具有那些东西,这表示封装(Rectangle具有sizeposition属性,这些属性是这些特定类的实例,或者,实际上, namedtuple s)而不是继承。封装肯定是“适当的OOP”。

答案 1 :(得分:1)

首先,我要回答你的问题:与has-a关系一起去。

class Rectangle:
    def __init__(self, size=None, position=None):
        self.size = size or Size()
        self.position = position or Point()

想一想:矩形的位置和大小,还是矩形 的位置和大小? 如果你正在编写一个只构建2D矩形的游戏,那么就有你的答案,停止阅读。


...但是,如果你想要更多的形状(例如Circle),你应该在做任何事情之前三思而后行。 编写OOP时,请始终考虑您的需求。 所以,最终我们可能还需要Circle课程。在编写Rectangle - 类之前,我们应该考虑这个和其他形状。

CircleRectangle都有一个名为position的公共属性。 这就是为什么你应该有一个名为ShapeGeometry的基类来定义至少position

class Geometry:
    def __init__(self, position=None):
        self.position = position or Point()

class Rectangle(Geometry):
    def __init__(self, size=None, position=None):
        self.size = size or Size()
        super().__init__(position)

class Circle(Geometry):
    def __init__(self, radius=0, position=None):
        self.radius = radius
        super().__init__(position)

还要考虑一下你可能拥有的其他类,看看它们是否有任何共同属性:

  • 三角
  • 五角
  • 其他多边形

你很快就会意识到他们都有position,这就是为什么我们Geometry。 它们还有一些size(线的长度,圆的半径,三角形的宽度和高度......),所以你也可以为不同的size创建几个基类(例如宽度和放大器) ;三角形和矩形的高度。)

答案 2 :(得分:0)

super()适用于非常具体的用例,即当您拥有具有相同__init__()变量的对象层次结构时,并且您无法完全控制对象层次结构时,我不知道它的外观。

然后,您使用super()

当您使用mixin类编写库时,这种情况正在实践中。虽然理论上可以这样做,但它会因为你不能拥有相同的__init__()参数而分解,然后super()根本不能很好地工作。

因此,忘记super()。这样做:

class Size(object):
    def __init__(self, width=0, height=0):
        self.width = width
        self.height = height

class Point(object):
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

class Rectangle(Size, Point):
    def __init__(self, x=0, y=0, width=0, height=0):
        Point.__init__(self, x, y)
        Size.__init__(self, width, height)

看不到超级。 (请注意,我将所有内容都从object继承,在Python 3中不需要它,但只要Python 2仍在广泛使用中,它就是很好的做法。)

但是,是的,由于某些原因,这会使用您不想要的四个参数。有两种解决方案。一个是马库斯提议,改为使用遏制:

class Rectangle(object):
    def __init__(self, size=None, position=None):
        self.size = size or Size()
        self.position = position or Point()

但这意味着你必须将Point()和Size()的所有方法都粘贴到Rectangle上。另一个解决方案是有点聪明,并使用一个非常有用的内置类型:复杂!

class Point(object):
    def __init__(self, pos=0j):
        self.pos = pos

class Size(object):
    def __init__(self, size=0j):
        self.size = size

class Rectangle(Size, Point):
    def __init__(self, pos=0j, size=0j):
        Point.__init__(self, pos)
        Size.__init__(self, size)

对2D图形使用复数简化了很多。我甚至不打算使用Point和Size类,我只是直接使用complex:

class Rectangle(object):
    def __init__(self, pos=0j, size=0j):
        self.pos = pos
        self.size = size

    def endpos(self):
        return self.pos + self.size


>>> r = Rectangle(5+3j, 3+7j)
>>> print(r.endpos())
(8+10j)

这也更好,因为正如收容建议一样, 的大小和位置,而不是 的大小和位置,这更有意义。< / p>