Object-oriented programming (of object-georiënteerd programmeren), afgekort OOP, is een veelgebruikte manier van programmeren. Hierbij wordt de code gestructureerd rondom objecten, waarbij data wordt opgeslagen als een attribuut van een object en logica wordt uitgevoerd in methodes die toebehoren aan een object.
In deze blog leggen we middels voorbeelden uit wat OOP is, wat de basisprincipes zijn en waarom dit nuttig is. We houden de volgende structuur aan:
- Wat is Object-Oriented Programming (OOP)?
- Object-Oriented Programming in Python
- De 4 principes van Object-Oriented Programming
- Wanneer gebruik ik Object Georiënteerd programmeren?
Wat is Object-Oriented Programming (OOP)?
Bij object-geörienteerd programmeren redeneer je vanuit objecten. Een object heeft eigenschappen en functies waarin logica uitgevoerd wordt op basis van de eigenschappen van een object. Een functie die aan een object toebehoort heet een method. Hiermee koppel je attributen en methods aan een object waardoor je code op een logische wijze groepeert.
Een voorbeeld: je bouwt een applicatie waarbij je data visualiseert en als een rapportage verzendt naar klanten. Een 'rapportage' en een 'klant' zijn in dit voorbeeld een object die de volgende eigenschappen zouden kunnen hebben:
- Rapportage: dataset, visualisatie, naam
- Klant: naam, e-mail adres
Daarnaast wil je een rapportage kunnen verzenden, bijvoorbeeld met een method send
die toebehoort aan het object 'rapportage'.
Object-Oriented Programming in Python
Objecten worden gedefinieërd door middel van een class, dit is de definitie waaraan een object voldoet. Op basis van deze class kan een object worden aangemaakt. Een object is een variabele in Python met als datatype de class.
De eigenschappen van een object kan worden vastgelegd in een dictionary, maar de mogelijkheden van een dictionary zijn beperkt ten opzichte van een class.
We gaan dit in een voorbeeld toelichten. Een class waarin alleen de naam van een rapportage is gedefinieerd maak je als volgt:
class Rapportage:
def __init__(self, naam):
self.naam = naam
test_rapportage = Rapportage(naam='Test')
Je ziet hier het volgende:
- De conventie is dat een classnaam altijd met een hoofdletter begint (in tegenstelling tot normale Python variabelen)
- Binnen de class is een method genaamd
__init__()
. Deze method wordt uitgevoerd als je een object maakt op basis van deze class. Het object wordt dan geïnitialiseerd (vandaar de term 'init'). - Het eerst argument van
__init__()
isself
, dit verwijst naar het object dat gecreëerd is. - De regel
self.naam = naam
zorgt ervoor dat het object de naam krijgt die je opgeeft. De waarde vantest_rapportage.naam
is nu gelijk aan'Test'
.
Eigenschappen van een object kan je benaderen via de 'dot notatie', bijvoorbeeld via test_rapportage.naam
, hetgeen gelijk is aan 'Test'
.
In het voorbeeld hierboven beschreven had een rapportage meer eigenschappen, deze voegen we toe. Daarnaast moet de rapportage verzonden kunnen worden.
import pandas as pd
class Rapportage:
def __init__(self, naam, df_dataset):
self.naam = naam
self.df_dataset = df_dataset
self.klanten = []
def send(self):
# in dit voorbeeld houden we het simpel en wordt het verzenden gesimuleerd middels print().
for klant in self.klanten:
print(f"Hallo {klant.naam}")
print(f"De dataset met {len(self.df_dataset.index)} regels wordt verstuurd naar {klant.email}.")
dataset = [
{ 'meetpunt': 1, 'waarde': 100 },
{ 'meetpunt': 2, 'waarde': 200 },
{ 'meetpunt': 3, 'waarde': 300 },
]
test_rapportage = Rapportage(naam='Test', df_dataset=pd.DataFrame(dataset))
Nu maken we ook nog de klant, deze heeft een naam en e-mail adres, en voegen dit toe aan de rapportage.
class Klant:
def __init__(self, naam, email):
self.naam = naam
self.email = email
klant_dsp = Klant(naam='Data Science Partners', email='info@datasciencepartners.nl')
test_rapportage.klanten.append(klant_dsp)
Vervolgens verzenden we de rapportage naar deze klant.
test_rapportage.send()
Hallo Data Science Partners
De dataset met 3 regels wordt verstuurd naar info@datasciencepartners.nl.
Bovenstaand voorbeeld beschrijft de werking van OOP in Python:
- In classes beschrijf je definities
- Objecten worden aangemaakt door een class aan te roepen
- Object-specifieke logica is in de class beschreven
Omdat classes je dwingen om logica te groeperen per object, schrijf je vaak automatisch betere code die beter schaalbaar en leesbaar is.
De 4 principes van Object-Oriented Programming
Object-geörienteerd programmeren kent in elke taal een aantal kenmerkende basisprincipes die OOP krachtig maken. Dit zijn:
- Inheritance (overerving)
- Encapsulation (inkapseling)
- Abstraction (abstraheren)
- Polymorphism (polymorfisme)
1. Inheritance (overerving)
Middels overerving kan je classes creëeren op basis van andere classes. Een voorbeeld:
class RapportageMetLijngrafiek(Rapportage):
def __init__(self, naam, df_dataset):
super().__init__(naam, df_dataset)
def get_visualization(self):
return self.df_dataset.plot(kind="line")
Je ziet hier dat we de class baseren op de reeds gedefinieerde class Rapportage
. In de __init__()
method roepen we __init__()
methode van de parent (Rapportage
) aan middels super()
.
Dit child class heeft daarnaast een method get_visualization()
waarmee een grafiek gemaakt kan worden. Deze method is op deze manier alleen beschikbaar voor objecten met class RapportageMetLijnGrafiek
. De overige eigenschappen zijn echter overgeërft van de class Rapportage
.
2. Encapsulation (inkapseling)
In classes kan je bepaalde data alleen beschikbaar maken binnen een object, zodat er geen uitwisseling van informatie kan plaats vinden. Hierdoor blijft logica gegarandeerd geïsoleerd. Dit kan je doen door een variabele te laten beginnen met twee underscores. We passen het eerder gebruikte voorbeeld aan door de variable df_dataset
te laten beginnen met twee underscores.
import pandas as pd
class Rapportage:
def __init__(self, naam, df_dataset):
self.naam = naam
self.__df_dataset = df_dataset
self.klanten = []
dataset = [
{ 'meetpunt': 1, 'waarde': 100 },
{ 'meetpunt': 2, 'waarde': 200 },
{ 'meetpunt': 3, 'waarde': 300 },
]
test_rapportage = Rapportage(naam='Test', df_dataset=pd.DataFrame(dataset))
De dataset is nu niet bereikbaar buiten het object: de variabele test_rapportage.__df_dataset
is niet beschikbaar en zal een error geven. Zonder de twee underscores had dit wel gewerkt. De error ziet er als volgt uit:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In [5], line 18
10 dataset = [
11 { 'meetpunt': 1, 'waarde': 100 },
12 { 'meetpunt': 2, 'waarde': 200 },
13 { 'meetpunt': 3, 'waarde': 300 },
14 ]
16 test_rapportage = Rapportage(naam='Test', df_dataset=pd.DataFrame(dataset))
---> 18 test_rapportage.__df_dataset
AttributeError: 'Rapportage' object has no attribute '__df_dataset'
3. Abstraction (abstraheren)
Door gebruik te maken van methods kan je logica abstraheren. In bovenstaande voorbeeld kan je een rapportage verzenden door de method .send()
aan te roepen. De logica daarvan definieer je 1x in de class, maar in de rest van de code hoef je niet na te denken over hoe het concept van iets 'verzenden' is doorvertaald naar code.
4. Polymorphism (polyformisme)
Polymorfisme betekent dat dezelfde functie of method, afhankelijk van de class, andere logica uitvoert. In het voorbeeld bij 'inheritance' staat hier een voorbeeld van. We kunnen ook nog een class RapportageMetStaafGrafiek
maken:
class RapportageMetLijngrafiek(Rapportage):
def __init__(self, naam, df_dataset):
super().__init__(naam, df_dataset)
def get_visualization(self):
return self.df_dataset.plot(kind="line")
class RapportageMetStaafgrafiek(Rapportage):
def __init__(self, naam, df_dataset):
super().__init__(naam, df_dataset)
def get_visualization(self):
return self.df_dataset.plot(kind="bar")
rapportage1 = RapportageMetLijngrafiek(naam='Lijngrafiek', df_dataset=dataset)
rapportage2 = RapportageMetStaafgrafiek(naam='Staafgrafiek', df_dataset=dataset)
De objecten rapportage1
en rapportage2
hebben beide een method get_visualization()
, maar zoals je ziet zal het ene object een lijngrafiek krijgen en de ander een staafgrafiek.
Wanneer gebruik ik Object Georiënteerd programmeren?
OOP is bijna altijd een goede keuze: je maakt dan automatisch gebruik van de hierboven beschreven principes waardoor code wordt versimpeld en beter leesbaar is. Er zijn echter ook situaties waarbij het minder nut heeft. Vaak merk je dit omdat het moeilijk is te bedenken wat de 'objecten' zouden moeten zijn, dit komt vaak voor in kleine projecten waarbij code (nog) geen herhalende of repeterende stappen bevat. Naarmate een project groeit verandert dit en kan het verstandig zijn om code te groeperen in objecten en classes.
Wil je nog veel meer leren over met mogelijkheden met Python voor Data Science? Schrijf je dan in voor onze Python cursus voor data science, onze machine learning training, onze data science opleiding of onze data science bootcamp en leer met vertrouwen te programmeren en analyseren in Python. Nadat je een van onze trainingen hebt gevolgd kun je zelfstandig verder aan de slag. Je kunt ook altijd even contact opnemen als je een vraag hebt.
Download één van onze opleidingsbrochures voor meer informatie
Terry is afgestudeerd aan de TU Delft als ingenieur en heeft zich in zijn carrière beziggehouden met het optimaal benutten van data om bedrijfsprestaties te verbeteren. Dit heeft hij gedaan in verschillende rollen, als software ontwikkelaar en als data scientist.