Skip to main content
deleted 1 character in body
Source Link
Graipher
  • 41.1k
  • 7
  • 69
  • 133

Normally I would probably make thesethem an actual property:

Normally I would probably make these an actual property:

Normally I would probably make them an actual property:

Source Link
Graipher
  • 41.1k
  • 7
  • 69
  • 133

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 these 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.