我正在尝试在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 parameters
和size
(不是让它们可选)但我宁愿不这样做。
我还可以将position
定义为与Rectangle
和has-a
而不是Size
关系建立Point
关系,但这甚至不是正确的OOP而我是主要是在这里学习,所以我宁愿不这样做。
答案 0 :(得分:3)
您使用super()
错误。您在调用中指定您自己的类,以转到下一个父类。你所有的其他课程都需要做同样的事情。你现在这样做的方式,你要说的是Size
的父类,object
。
这是Python 3,你可以在没有任何参数的情况下进行super()
,它会为你解决所有问题。
当然,这意味着您无法使用super()
显式调用两个父类,因为给定的类只有一个super()
。如果您真的想这样做,则必须将其称为Size.__init__(self, ...)
,依此类推。
但我不得不说,你已经制定的继承计划没有任何意义。矩形既不是一种尺寸也不是一种点。相反,它具有那些东西,这表示封装(Rectangle
具有size
和position
属性,这些属性是这些特定类的实例,或者,实际上, 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
- 类之前,我们应该考虑这个和其他形状。
Circle
和Rectangle
都有一个名为position
的公共属性。
这就是为什么你应该有一个名为Shape
或Geometry
的基类来定义至少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>