A list of objects as an attribute
You have already written classes that keep a list. Often that list holds objects of another class of yours: a playlist has songs, a team has players, a shopping cart has products. The usual pattern has three parts:
__init__creates an empty list:self.songs = [].- A method adds an object to the list.
- Other methods go through the list to work out something about all the objects.
class Song:
def __init__(self, title, seconds):
self.title = title
self.seconds = seconds
def __str__(self):
return f"{self.title} ({self.seconds} s)"
class Playlist:
def __init__(self, name):
self.name = name
self.songs = []
def add(self, song):
self.songs.append(song)
def total_seconds(self):
total = 0
for song in self.songs:
total += song.seconds
return total
def print_songs(self):
print(f"{self.name}: {len(self.songs)} songs, {self.total_seconds()} s")
for song in self.songs:
print(song)
road_trip = Playlist("Road trip")
road_trip.add(Song("Morning Light", 215))
road_trip.add(Song("Neon Rain", 187))
road_trip.print_songs()
Road trip: 2 songs, 402 s
Morning Light (215 s)
Neon Rain (187 s)Each playlist gets its own empty list when it's created, because __init__ runs once for every new object. Every method reaches the list as self.songs.
A method can call another method of the same object, with self. in front: print_songs uses self.total_seconds() instead of adding up the seconds a second time. Writing each calculation once, in its own method, keeps the class short and easy to change.
Forgetting self.
Inside a method, the list is self.songs, never plain songs. Without self., Python looks for a local variable called songs, which doesn't exist:
class Playlist:
def __init__(self, name):
self.name = name
self.songs = []
def add(self, song):
songs.append(song)
mix = Playlist("Mix")
mix.add("Neon Rain")
Traceback (most recent call last):
File "main.py", line 11, in <module>
mix.add("Neon Rain")
~~~~~~~^^^^^^^^^^^^^
File "main.py", line 7, in add
songs.append(song)
^^^^^
NameError: name 'songs' is not defined. Did you mean: 'self.songs'?The traceback shows two places: line 11, the call, and line 7 inside add, where the error really happened. The last place listed is usually the one to fix, and here Python even suggests the fix.