None: no object
Sometimes there is no object to give. A search finds nothing, or a parking spot has no car in it yet. For that, Python has the special value None, which means "nothing here". You have met it before: it's what a function returns when it has no return. You can also return it, or store it, on purpose.
A search that may find nothing
A method that looks for an object can return None when there's no match:
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):
self.songs = []
def add(self, song):
self.songs.append(song)
def find(self, title):
for song in self.songs:
if song.title == title:
return song
return None
mix = Playlist()
mix.add(Song("Neon Rain", 187))
print(mix.find("Neon Rain"))
print(mix.find("Blue Hour"))
Neon Rain (187 s)
NoneThe return song inside the loop ends the method as soon as the song is found. The return None after the loop only runs when the loop went through every song without finding it.
The error you will meet
The trouble starts when the caller forgets that the answer can be None, and uses it as if it were an object. With the same classes as above:
class Song:
def __init__(self, title, seconds):
self.title = title
self.seconds = seconds
class Playlist:
def __init__(self):
self.songs = []
def find(self, title):
for song in self.songs:
if song.title == title:
return song
return None
mix = Playlist()
song = mix.find("Blue Hour")
print(song.seconds)
Traceback (most recent call last):
File "main.py", line 20, in <module>
print(song.seconds)
^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'seconds'NoneType is the type of None. So whenever you see 'NoneType' object has no attribute, the thing before the dot was None instead of the object you expected. Look for where that value came from: here, a search that found nothing.
Check before you use it
Test the answer with is None or is not None before you use it. There is only one None in the whole program, so is is the natural way to ask about it:
song = mix.find("Blue Hour")
if song is None:
print("Not on the playlist")
else:
print(song.seconds)
An attribute that starts out empty
An attribute can be None until it gets an object. A parking spot starts without a car:
class Car:
def __init__(self, plate):
self.plate = plate
class ParkingSpot:
def __init__(self, number):
self.number = number
self.car = None
def park(self, car):
if self.car is not None:
return False
self.car = car
return True
def __str__(self):
if self.car is None:
return f"Spot {self.number}: free"
return f"Spot {self.number}: {self.car.plate}"
spot = ParkingSpot(7)
print(spot)
print(spot.park(Car("ABC-123")))
print(spot.park(Car("XYZ-789")))
print(spot)
Spot 7: free
True
False
Spot 7: ABC-123