0

我正在尝试Rectangle在 python 中创建一个简单的 -class,但我还需要在我的代码中使用点和大小,所以我试图继承我Rectangle的 fromPointSize. 问题是,我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 parameters

有没有更清洁的方法来做到这一点?我当然可以强迫我的矩形采用sizeand position(不要让它们成为可选的),但我宁愿不这样做。我也可以将 my 定义Rectanglehas-a与关系SizePoint不是is-a关系,但这甚至不是正确的 OOP,我主要在这里学习,所以我也不想这样做。

4

3 回答 3

3

你用super()错了。您在调用中指定自己的类,以获取下一个父类。你所有的其他类都需要做同样的事情。你现在这样做的方式是调用 egSize的父类,即object.

这是 Python 3,您super()无需任何参数即可完成,它会为您解决所有问题。

当然,这意味着您不能super()像您想要的那样使用 显式调用两个父类,因为给定的类只有一个super(). 如果您真的想这样做,则必须将它们称为Size.__init__(self, ...)等等。

但不得不说,你炮制的继承方案没有任何意义。矩形既不是一种大小,也不是一种点。相反,它具有那些表示封装(其中 aRectangle具有sizeposition属性是那些特定类的实例,或者实际上是namedtuples)而不是继承的东西。封装当然是“正确的 OOP”。

于 2013-04-26T15:21:02.157 回答
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-class 之前,我们应该考虑这个和其他形状。

Circle并且Rectangle两者都有一个共同的属性,称为position. 这就是为什么您应该有一个名为ShapeGeometry定义 atleast的基类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的创建几个基类(例如三角形和矩形的宽度和高度)。

于 2013-04-26T15:31:14.950 回答
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)

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

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

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 and 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)

这也更好,因为正如收容建议一样,它具有大小和位置,而不是大小和位置,这更有意义。

于 2013-04-26T19:12:12.060 回答