Python · Syntax · Beginner
OOP: Inheritance and Dunder Methods
OOP problems in Python on inheritance and special methods: child classes, overriding behavior, __str__, __len__, extending base classes, and working with the object model.
Quick topic start and explanations before exercises (exercises below):
Dunder methods — __str__ and __len__
#Inheritance and dunder patterns
#Inheritance and dunder methods quick reference
#Exercises:
Animal and Dog through inheritance.
#class Animal:
pass
class Dog(Animal):
pass
Solution
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __str__(self):
return f"Dog: {self.name}"
# You can make it more detailed:
class Dog(Animal):
def __str__(self):
return f"Dog: {self.name}"
def tell(self):
print("Bark!!!") # "Woof" in English. Dogs also need a translator..
class Cat(Animal):
def __str__(self):
return f"Cat: {self.name}"
def tell(self):
print("Cats don't care about human commands!")
# self still needs to be accepted even if it is not used in the method.
Book with string representation.
#class Book:
pass
Solution
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __len__(self):
return self.pages
def __str__(self):
return f"Book: {self.title} ({self.pages} pages)"
User and Admin.
#class User:
pass
class Admin(User):
pass
Solution
class User:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
class Admin(User):
def __str__(self):
return f"Admin: {self.name}"
Box with items.
#class Box:
pass
Solution
class Box:
def __init__(self, items_count):
self.items_count = items_count
def add(self, count=1):
self.items_count += count
def remove(self, count=1):
self.items_count -= count
def __len__(self):
return self.items_count
# With a list it is more complicated and different:
class Box:
def __init__(self):
self.items = []
self.items_count = len(self.items)
def add(self, item):
self.items.append(item)
self.items_count = len(self.items)
def remove(self, item):
if item in self.items:
self.items.remove(item)
self.items_count = len(self.items)
else:
print("The cat from the box says: There is no such thing here, goodbye!")
def __len__(self):
return self.items_count
Message with a spam flag.
#class Message:
pass
Solution
class Message:
def __init__(self, text):
self.text = text
self.spam = None
self.spam_triggers = ["куплю", "дорого"] # Yes, I did not talk about this,
# but I did not forbid it either.
def is_spam(self):
if self.spam is not None: # explanation below, I already "overdid" it here
return self.spam
for trigger in self.spam_triggers:
if trigger in self.text:
self.spam = True
return True
self.spam = False
return False
def __len__(self):
return len(self.text)
# Having self.spam after the first call to is_spam will allow repeated checks
# to skip the checking mechanism and simply look at the ALREADY CALCULATED attribute (optimization).
m = Message("Куплю старые телевизоры дорого!!!")
if m.is_spam(): # a real check is performed
...
if m.is_spam(): # it will already be taken from self.spam
...
Employee and Manager.
#class Employee:
pass
class Manager(Employee):
pass
Solution
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def change_salary(self, coef): # for example coefficient 1.1 - increase by 10%
self.salary = round(self.salary * coef) # without the second argument it rounds to an integer
class Manager(Employee):
def __str__(self):
return f"Manager {self.name} earns {self.salary}"
chel = Manager("Bob", 1000)
chel.change_salary(1.1)
print(chel) # Manager Bob earns 1100
Playlist and CustomPlaylist.
#class Playlist:
pass
Solution
class Playlist:
def __init__(self, songs): # 1 argument - a list of tracks
self.songs = songs
def add(self, song):
if song not in self.songs:
self.songs.append(song)
def remove(self, song):
if song in self.songs:
self.songs.remove(song)
def reverse(self):
self.songs.reverse()
def __len__(self):
return len(self.songs)
def __bool__(self):
return bool(self.songs)
# or
# class Playlist:
# def __init__(self, *songs): # tracks as separate arguments
# self.songs = list(songs) # just decided to remind you about *args
# # the rest is the same.
class CustomPlaylist(Playlist):
def add(self, song):
self.songs.append(song)
Named CustomPlaylist.
#class CustomPlaylist(Playlist):
def __init__???
???
def add(self, song):
self.songs.append(song)
Solution
class CustomPlaylist(Playlist):
def __init__(self, songs, name):
super().__init__(songs)
self.name = name
def add(self, song):
self.songs.append(song)
def __str__(self):
return f"Playlist: {self.name}"
MagicBox with a random item.
#from random import choice # function for random choice from a sequence
class Box:
def __init__(self):
self.items = []
self.items_count = len(self.items)
def add(self, item):
self.items.append(item)
self.items_count = len(self.items)
def remove(self, item):
if item in self.items:
self.items.remove(item)
self.items_count = len(self.items)
else:
print("Кот из коробки говорит: Такого тут нет, досвидания!")
def __len__(self):
return self.items_count
things = ["Шапка", "Зонтик", "Кружка"]
class MagicBox(Box):
def __init__(self):
...
Solution
from random import choice # function for random choice from a sequence
class Box:
def __init__(self):
self.items = []
self.items_count = len(self.items)
def add(self, item):
self.items.append(item)
self.items_count = len(self.items)
def remove(self, item):
if item in self.items:
self.items.remove(item)
self.items_count = len(self.items)
else:
print("Кот из коробки говорит: Такого тут нет, досвидания!")
def __len__(self):
return self.items_count
things = ["Шапка", "Зонтик", "Кружка"]
class MagicBox(Box):
def __init__(self):
super().__init__()
self.items.append(choice(things))
self.items_count = len(self.items)
# OR:
class MagicBox(Box):
def __init__(self):
super().__init__()
self.add(choice(things)) # this is cooler
x = MagicBox()
print(x.items)