Rather than making perimeter
and field
attributes, you should implement them as properties, instead. That way, they are only calculated when called for. This will also prevent those values from having to be calculating when you instantiate the class.
As also mentioned, inheriting Square from Rectangle is useful. Square, essentially, could just be an alternate constructor for Rectangle. Though you may opt for a different design, depending on the use case.
class Rectangle(Shape):
def __init__(self, x, y, a, b):
self.a = a
self.b = b
super().__init__(x, y)
@property
def perimeter(self):
return (self.a + self.b) * 2
class Square(Rectangle):
'''Just an alternate constructor for Rectangle'''
def __init__(self, x, y, a):
super().__init__(x, y, a, a) #b is same as a in a square.
#everything else already works!
Example:
>>> my_square = Square(0, 0, 10)
>>> my_square.perimeter
40
>>> my_rect = Rectangle(0,0, 10, 5)
>>> my_rect.perimeter
30
Another problem properties solve is that users cannot erroneously set the perimeter to a different value.
>>> my_square.perimeter = 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Otherwise, perimeter
would be allowed to be set and would therefore be inconsistent with the values of a
and b
#without properties you could get inconsistent values
>>> my_square.perimeter = 10
>>> print(my_square.perimeter, my_square.a, my_square.b)
10 10 10
You could also use property setters to dictate what happens when someone tries to set a property.
class Circle(object):
def __init__(self, radius):
self.radius = radius
@property
def diameter(self):
return self.radius * 2
@diameter.setter
def diameter(self, diam):
self.radius = diam / 2
Ex:
>>> my_circle = Circle(radius=5)
>>> my_circle.diameter
10
>>> my_circle.diameter = 40
>>> my_circle.radius
20