Python Arena
Step 3 of 4

Default values for parameters

In part 7 you gave a function's parameters default values, used whenever the caller leaves the argument out. Methods and constructors can have them too. They're handy when most objects start the same way:

class Character:
    def __init__(self, name, health=100, level=1):
        self.name = name
        self.health = health
        self.level = level

    def heal(self, amount=10):
        self.health += amount

    def __str__(self):
        return f"{self.name}: health {self.health}, level {self.level}"


ana = Character("Ana")
bo = Character("Bo", 80)
cy = Character("Cy", level=5)
bo.heal()
bo.heal(5)
print(ana)
print(bo)
print(cy)
Output
Ana: health 100, level 1
Bo: health 95, level 1
Cy: health 100, level 5

Arguments fill the parameters in order, so Character("Bo", 80) sets the health. To give a later parameter and skip an earlier one, name it: Character("Cy", level=5) is a keyword argument. Writing Character("Cy", 5) would set the health to 5, not the level.

Parameters with default values come after the ones without. def __init__(self, health=100, name): is a syntax error: parameter without a default follows parameter with a default.

The trap: a list as a default value

A default value is created only once, when Python reads the def line, not on every call. For a number or a string that never matters. For a list it does, because a list can change. Every object that gets the default ends up with the same list:

class Backpack:
    def __init__(self, owner, items=[]):
        self.owner = owner
        self.items = items

    def pack(self, item):
        self.items.append(item)


ana = Backpack("Ana")
bo = Backpack("Bo")
ana.pack("map")
print(bo.items)
print(ana.items is bo.items)
Output
['map']
True

Ana packed a map, and it turned up in Bo's backpack, because both backpacks refer to the one list that was made for the default.

The fix is to use None as the default, and create a new list inside the method when the caller didn't give one:

class Backpack:
    def __init__(self, owner, items=None):
        self.owner = owner
        if items is None:
            self.items = []
        else:
            self.items = items

    def pack(self, item):
        self.items.append(item)


ana = Backpack("Ana")
bo = Backpack("Bo")
ana.pack("map")
print(bo.items)
print(Backpack("Cy", ["tent"]).items)
Output
[]
['tent']

Now self.items = [] runs on every call, so every backpack gets a list of its own. Never use a list (or a dictionary) as a default value; use None and create it in the method.

Your task

Challenge 1 of 3

A meeting lasts 60 minutes unless told otherwise. Fill in the blank so that minutes has the default value 60, and the program prints:

Standup: 15 min
Planning: 60 min

Challenge 2

Two teams are created without any players, and then a player joins one of them, but she shows up in both teams. Fix the class so that every team gets a list of its own, while Team("Greens", ["Kim"]) still starts with the players given. The program should print:

Reds: Ada
Blues:
Greens: Kim, Lou

Challenge 3

Write the class Alarm. Its constructor takes the hour, the minute (by default 0) and a label (by default "Alarm"), and keeps them in the attributes hour, minute and label. The method snooze(self, minutes=5) moves the alarm later by the given number of minutes; when the minutes reach 60, the hour goes up by one (and after hour 23 comes hour 0). __str__ returns a string such as Wake up at 7:05, with the minutes always written with two digits.

For example, this code:

early = Alarm(7, label="Wake up")
print(early)
early.snooze()
print(early)
late = Alarm(23, 50)
late.snooze(15)
print(late)

prints:

Wake up at 7:00
Wake up at 7:05
Alarm at 0:05