Problem overview
A class bundles some values with the questions you can ask about them. Here the values are a rectangle's width and height, and the questions are its area, its perimeter and whether it is a square. You write the class Rectangle with a constructor and three methods: area(), perimeter() and is_square().
For Rectangle(3, 4) the area is 3 * 4 = 12 and the perimeter is 3 + 4 + 3 + 4 = 14. The sides differ, so it is not a square. For Rectangle(5, 5) the area is 25, the perimeter is 20 and the answer to the square question is True. The lesson's tests print the booleans as True or False.
Approach: each object keeps its own sizes
Every rectangle you create needs to remember its own width and height. In Python the constructor is the method __init__, and self is the object being created. Writing self.width = width stores the number on that one object, so a second rectangle gets its own copy and the two never mix.
The other methods can't see the parameters of __init__: those variables disappear when the constructor ends. They read the sizes back through self, which is why the constructor has to store them as attributes first.
Step by step
- Store the width as
self.widthinside__init__. - Store the height as
self.heighton the next line. areareturns the width times the height. A 3 by 4 rectangle is 3 rows of 4 unit squares, 12 in all.perimeterwalks around the rectangle: width, height, width, height. That is twice the sum of the two sizes, so the addition goes in parentheses and the result is multiplied by 2.is_squarereturns the comparison of the two sizes with==. A comparison already producesTrueorFalse, so noifis needed.
The solution in Python
class Rectangle:
def __init__(self, width: int, height: int):
self.width = width
self.height = height
def area(self) -> int:
return self.width * self.height
def perimeter(self) -> int:
return 2 * (self.width + self.height)
def is_square(self) -> bool:
return self.width == self.height
Creating rectangles and asking them questions looks like this:
for w, h in [(3, 4), (5, 5), (2, 10), (10, 2)]:
r = Rectangle(w, h)
print(w, h, r.area(), r.perimeter(), r.is_square())
It prints:
3 4 12 14 False
5 5 25 20 True
2 10 20 24 False
10 2 20 24 False
The last two rectangles are the same shape turned on its side, so the area and perimeter don't change.
Python details
selfis the first parameter of every method, but you don't pass it.r.area()handsrto the method asselfautomatically.- The annotations are hints.
width: intand-> intdocument what the code expects. Python doesn't enforce them. - Attributes are public. Python has no private fields, so code outside the class can read and change
r.width. Aftera = Rectangle(3, 4)anda.width = 6,a.area()is 24, while another rectangleRectangle(5, 5)still has area 25.
Edge cases and common mistakes
- Forgetting
self.in the constructor. A line likewidth = widthonly sets a local variable, and the object keeps nothing. - Leaving out the parentheses in
perimeter.2 * self.width + self.heightdoubles only the width. For a 3 by 4 rectangle that gives 10, not 14. - Forgetting the parentheses on a call.
r.areais the method itself, not its result. Always writer.area(). - Using an
ifforis_square. It works, butreturn self.width == self.heightsays the same in one line. - Complexity. Each method does a fixed amount of arithmetic, so each runs in O(1) time.
