Python OOP Concepts
OOP Concepts in PyThon
Introduction
This series of tutorials is to help anyone who might need to review some OOP concepts in Python. I will try to add diagrams and code to explain more intuitively.
ClassMethods
class Database:
content = {'users': []}
@classmethod
def insert(cls, data):
cls.content['users'].append(data)
@classmethod
def remove(cls, finder):
cls.content['users'] = [user for user in cls.content['users'] if not finder(user)]
@classmethod
def find(cls, finder):
return [user for user in cls.content['users'] if finder(user)]
@classmethod
def print_content(cls):
return cls.content
Since content is a class variable, not an instance variable; hence, it will not change per object. If we create multiple objects of type database, they will all have the same content variable.
d1 = Database()
d2 = Database()
print(d1.print_content())
print(d2.print_content())
OUTPUT:
> {'users': []}
> {'users': []}
d1.insert('example1')
print(d1.print_content())
print(d2.print_content())
OUTPUT:
> {'users': ['example1']}
> {'users': ['example1']}
d2.insert('example2')
print(d1.print_content())
print(d2.print_content())
OUTPUT:
> {'users': ['example1', 'example2']}
> {'users': ['example1', 'example2']}
Inheritance
#user.py
class User:
def __init__(self, username, password):
self.username = username
self.password = password
def login(self):
return 'Logged in!'
def __repr__(self):
return f'<User {self.username}>'
#saveable.py
class Saveable:
def save(self):
Database.insert(self.to_dict())
# databse.py
class Database:
content = {'users': []}
@classmethod
def insert(cls, data):
Database.content['users'].append(data)
# or cls.content['users'].append(data)
@classmethod
def remove(cls, finder):
cls.content['users'] = [user for user in cls.content['users'] if not finder(user)]
@classmethod
def find(cls, finder):
return [user for user in cls.content['users'] if finder(user)]
#admin.py
class Admin(User, Saveable):
def __init__(self, username, password, access):
super(Admin, self).__init__(username, password)
self.access = access
def __repr__(self):
return f'<Admin {self.username}, access {self.access}>'
def to_dict(self):
return {
'username': self.username,
'password': self.password,
'access': self.access
}
So in the above example, we have Admin class inheriting from both User and Saveable class. From app.py, we will create an object of Admin class.
#app.py
a = Admin('rolf', '1234', 3)
a.save()
print(Database.content)
print(Database.find(lambda x:x['username'] == 'rolf'))
OUTPUT:
<Admin rolf, access 3>
{'users': [{'username': 'rolf', 'password': '1234', 'access': 3}]}
[{'username': 'rolf', 'password': '1234', 'access': 3}]
Abstract Base Class
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
def walk(self):
print('walking..')
@abstractmethod
def num_legs(self):
pass
Since num_legs in Animal is an abstract class, all child classes that inherit Animal needs to implement the method num_legs.
class Dog(Animal):
def __init__(self, name):
self.name = name
def num_legs(self):
return 4
class Monkey(Animal):
def __init__(self, name):
self.name = name
def num_legs(self):
return 2
dog = Dog('Grover')
print(dog.num_legs())
OUTPUT:
> 4
ABCs and Interfaces
from abc import ABCMeta, abstractmethod
class Saveable(metaclass=ABCMeta):
def save(self):
Database.insert(self.to_dict())
@abstractmethod
def to_dict(self):
pass
# databse.py
class Database:
content = {'users': []}
@classmethod
def insert(cls, data):
Database.content['users'].append(data)
# or cls.content['users'].append(data)
@classmethod
def remove(cls, finder):
cls.content['users'] = [user for user in cls.content['users'] if not finder(user)]
@classmethod
def find(cls, finder):
return [user for user in cls.content['users'] if finder(user)]
Since to_dict is an abstract method, any class that will inherit from Saveable will have to implement the to_dict method. In our case, both User and Admin inherits Saveable, so they both have to implement the to_dict method.
#user.py
class User(Saveable):
def __init__(self, username, password):
self.username = username
self.password = password
def login(self):
return 'Logged in!'
def __repr__(self):
return f'<User {self.username}>'
def to_dict(self):
return {
'username': self.username,
'password': self.password
}
#admin.py
class Admin(User, Saveable):
def __init__(self, username, password, access):
super(Admin, self).__init__(username, password)
self.access = access
def __repr__(self):
return f'<Admin {self.username}, access {self.access}>'
def to_dict(self):
return {
'username': self.username,
'password': self.password,
'access': self.access
}
#app.py
a = Admin('rolf', '1234', 3)
a.save()
print(Database.content)
print(Database.find(lambda x:x['username'] == 'rolf'))
OUTPUT:
{'users': [{'username': 'rolf', 'password': '1234', 'access': 3}]}
[{'username': 'rolf', 'password': '1234', 'access': 3}]