Python Arena
Step 1 of 4

Methods in a class

In the previous module, a Room worked out its area in the constructor and kept it in an attribute. That number is right when the room is made, and wrong as soon as the room changes:

class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length
        self.area = width * length


kitchen = Room("Kitchen", 3, 4)
kitchen.width = 5
print(kitchen.width, kitchen.length, kitchen.area)
Output
5 4 12

A room 5 meters by 4 isn't 12 square meters. The area shouldn't be stored at all: it should be worked out when someone asks for it, from the width and the length the room has at that moment. That's a job for a method, a function that is part of the class and works with the attributes of the object it is called on.

A method is defined inside the class, as the constructor is, and its first parameter is self, the object it is called on:

class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    def area(self):
        return self.width * self.length

    def describe(self):
        return f"{self.name}: {self.area()} square meters"


kitchen = Room("Kitchen", 3, 4)
print(kitchen.area())
kitchen.width = 5
print(kitchen.area())
print(kitchen.describe())
Output
12
20
Kitchen: 20 square meters
  • The methods are indented inside the class, with one blank line between them.
  • When you call kitchen.area(), Python passes kitchen to the method as self, so self.width inside the method is the kitchen's width.
  • area works out the area from the attributes as they are when it is called. So, unlike an area stored in the constructor, it is never out of date: after the width changes, the next call gives the new area.
  • A method can call another method of the same object through self: describe calls self.area().

Methods with parameters

A method can take arguments after self, as any function can:

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def sale_price(self, percent):
        return self.price * (100 - percent) / 100


lamp = Product("Desk lamp", 40)
print(lamp.sale_price(25))
print(lamp.sale_price(10))
Output
30.0
36.0

lamp.sale_price(25) has one argument in the parentheses, and inside the method self is the lamp and percent is 25.

Forgetting self

Every method needs self as its first parameter. Leave it out, and the call fails, because Python always passes the object:

class Room:
    def __init__(self, width, length):
        self.width = width
        self.length = length

    def area():
        return self.width * self.length


hall = Room(2, 6)
print(hall.area())
Python stops with
Traceback (most recent call last):
  File "main.py", line 11, in <module>
    print(hall.area())
          ~~~~~~~~~^^
TypeError: Room.area() takes 0 positional arguments but 1 was given

The "1 given" is the object itself. Inside a method, the attributes also need self. in front: a plain width is a NameError, and Python's message asks "Did you mean: 'self.width'?"

Your task

Challenge 1 of 3

Fill in the blanks, so that the class has a method diameter and the program prints 60.

Challenge 2

Write a class Temperature whose constructor takes a temperature in degrees Celsius and stores it in the attribute celsius. Give it two methods:

  • fahrenheit() returns the temperature in degrees Fahrenheit: the Celsius degrees times 9, divided by 5, plus 32.
  • is_freezing() returns True when the temperature is 0 degrees Celsius or less, and False otherwise.

This code:

noon = Temperature(25)
night = Temperature(-10)
print(noon.fahrenheit(), noon.is_freezing())
print(night.fahrenheit(), night.is_freezing())

prints

77.0 False
14.0 True

Challenge 3

Write a class Password whose constructor stores the password in the attribute text. Give it three methods:

  • length() returns the number of characters in the password.
  • has_digit() returns True when the password has at least one digit (0 to 9) in it.
  • is_strong() returns True when the password is at least 8 characters long and has a digit. Use the other two methods to find that out.

This code:

weak = Password("sunshine")
strong = Password("tr4in-ride")
print(weak.length(), weak.has_digit(), weak.is_strong())
print(strong.length(), strong.has_digit(), strong.is_strong())

prints

8 False False
10 True True