First, the docstring
goes inside of the class definition:
class Circle(Shape):
"""
Circle class attributes must be:
radius=x
"""
...
This allows you to e.g. call help(Circle)
in an interactive shell and get back this string. This is documented in PEP257.
To answer your question how the user is to know what the parameters are, just make them explicit in the constructors:
class Circle(Shape):
def __init__(self, radius):
super().__init__(radius=radius)
self.area = pi * radius**2
self.perimeter = 2 * pi * radius
This way it is actually useful to override the __init__
method. If all you do is call the parents constructor (without any change of arguments) you could just leave it out.
Note that I also moved the calculation of perimeter and area into the constructor. This way the class is usable right after construction and you don't need to call some arcane calculate this property function.
I also fixed your calculation for the perimeter and area of the circle.
Normally I would probably make them an actual property
:
class Circle(Shape):
def __init__(self, radius):
super().__init__(radius=radius)
@property
def area(self):
return pi * self.radius**2
@property
def perimeter(self):
return 2 * pi * self.radius
But this has the disadvantage that you cannot discover it from the __dict__
anymore. But then again, these are two fixed names, so you could hardcode them in Shape.__str__
:
class Shape:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def __str__(self):
all_info = [self.__class__.__name__]
for attribute, value in self.__dict__.items():
if value > 0:
all_info.append(f"{attribute}: {value:.2f}")
all_info.append(f"{area}: {self.area:.2f}")
all_info.append(f"{perimeter}: {self.perimeter:.2f}")
return "\n".join(all_info)
Note that I removed your string addition, because it is very costly in Python (strings are immutable, so adding two strings involves allocating a new string of the correct size and copying the content of the two strings there), especially if you do many of them, as you do here. Instead I used a list of strings, which I str.join
in the end.
I also used the new f-string
to make the formatting a lot easier to read.