Python Classes

T. Issaris

22 Februari 2021

Groeperen

  • Groeperen van code: functies
  • Groeperen van data: lijsten en dictionaries
  • Groeperen van data en code?

Definitie class

Keyword class

class Hond:
    pass

Properties

Hoe voegen we data toe?

class Hond:
    naam = "Odie"
    leeftijd = 2

Een variabele in een class noemen we een property.

Oefening 1

Definieer een class Auto met als properties bouwjaar, naam en topsnelheid.

Instance

Met een class maak je instances van je class ook wel objecten genoemd:

class Hond:
    naam = "Odie"
    leeftijd = 2

hond = Hond()

Instance afdrukken

Als je je object afdrukt, zegt Python je dat hond een object is van het type Hond, in de hoofdmodule __main__.

class Hond:
    naam = "Odie"
    leeftijd = 2

hond = Hond()
print(hond) # toont <__main__.Hond object at 0x7f53b9a87970>

Meerdere instances

Je kan ook meerdere instances van je class aanmaken:

class Hond:
    naam = "Odie"
    leeftijd = 2

hond1 = Hond()
hond2 = Hond()
print(hond1)  # toont <__main__.Hond object at 0x7f53b9a87970>
print(hond2)  # toont <__main__.Hond object at 0x7f53b9a87970>

hond1 en hond2 zijn objecten van class Hond.

De dot-operator

Met de “.” operator krijg je toegang tot de properties van je objecten:

class Hond:
    naam = "Odie"
    leeftijd = 2

hond = Hond()
print(hond.naam)  # toont Odie
print(hond.leeftijd) # toont 2

Properties per instance

Properties worden per instance bijgehouden, properties worden dus niet gedeeld:

class Hond:
    naam = "Odie"
    leeftijd = 2

hond1 = Hond()
hond2 = Hond()
hond2.naam = "Max"
print(hond1.naam)  # toont Odie
print(hond2.naam)  # toont Max

Hoewel property hond2.naam gewijzigd werd, blijft property hond1.naam onaangepast.

Oefening 2

Kopieer de code uit oefening 1. Pas de code aan zodat je twee objecten aanmaakt model3 en taycan. Pas de properties aan zodat ze overeenkomen met de Tesla Model 3 en Porsche Taycan.

Stempel

Een class kan je zien als een stempel: Net zoals de vorm van de stempel bepaalt wat voor afdrukken je ervan kunt maken, bepaalt de class wat voor objecten je kan maken.

Code in een class

Hoe voegen we code toe?

class Hond:
    def maak_geluid(self):
        print("Woef!")

Een functie in een class noemen we een method.

Oproepen method

We kunnen deze method nu oproepen op onze objecten:

class Hond:
    def maak_geluid(self):
        print("Woef!")

dier = Hond()
dier.maak_geluid() # toont Woef!

Parameter self

Onze method kan properties gebruiken via de speciale eerste parameter van elke method (bij conventie self genaamd):

class Hond:
    naam = "Odie"

    def maak_geluid(self):
        print(self.naam + " zegt: Woef!")

dier = Hond()
dier.maak_geluid()  # Odie zegt: Woef!

Self voor toegang tot eigen properties

class Hond:
    naam = "Odie"

    def maak_geluid(self):
        print(self.naam + " zegt: Woef!")

dier1 = Hond()
dier1.naam = "Bobby"
dier2 = Hond()
dier2.naam = "Max"
dier1.maak_geluid()  # Bobby zegt: Woef!
dier2.maak_geluid()  # Max zegt: Woef!

Oefening 3

Kopieer de code uit oefening 2. Voeg een method toon_eigenschappen toe die zowel het bouwjaar, de naam als de topsnelheid afdrukt. Gebruik de method toon_eigenschappen op beide objecten model3 en taycan met behulp van de dot-operator.

Magic methods

Functienamen die beginnen en eindigen met __ worden magische functies genoemd.

Magic method: str

Als we een object proberen af te drukken, krijgen we volgende uitvoer:

class Hond:
    naam = "Odie"

dier = Hond()
print(dier) # <__main__.Hond object at 0x7f7a3f206970>

Magic method: str

__str__ is een magische functie die gebruikt wordt om een object naar een string te converteren.

class Hond:
    naam = "Odie"
    def __str__(self):
        return self.naam

dier = Hond()
print(dier) # Odie

Oefening 4

Kopieer de code uit oefening 3. Voeg de magic method __str__ toe die de naam en het bouwjaar van de Auto teruggeeft met behulp van een f-string.

Print beide objecten model3 en taycan en merk op dat de naam en het bouwjaar van de auto afgedrukt worden.

Magic method: init

__init__ is een andere magische functie die gebruikt wordt om de inhoud van een object te initialiseren.

class Hond:
    def __init__(self):
        self.naam = "Odie"

dier = Hond()
print(dier.naam) # Odie

Parameters voor init

Je kan __init__ parameters meegeven:

class Hond:
    def __init__(self, naam):
        self.naam = naam

dier1 = Hond("Max")
dier2 = Hond("Sam")

print(dier1.naam) # Max
print(dier2.naam) # Sam

Oefening 5

Kopieer de code uit oefening 4. Voeg de magic method __init__ toe die de naam van de auto als parameter neemt, en deze in self.naam opslaat.

Magic method: init

We kunnen dan via self verwijzen naar onze eigen property naam:

class Hond:
    def __init__(self, naam):
        self.naam = naam

    def maak_geluid(self):
        print(self.name, " zegt: Woef!")

dier1 = Hond("Max")
dier2 = Hond("Sam")

dier1.maak_geluid() # Max zegt: Woef!
dier2.maak_geluid() # Sam zegt: Woef!

Oefening 6a

Kopieer de code uit oefening 5.

Voeg de method toon_informatie toe die de naam, het bouwjaar en de topsnelheid op het scherm afdrukt. Voer de methode voor de objecten model3 en taycan uit.

Oefening 6b

Kopieer de code uit oefening 6a.

Voeg de method is_snel toe die True teruggeeft als de auto sneller dan 200 km/h kan rijden, en False in het andere geval.

Meerdere parameters

Je kan ook meerdere parameters voorzien in “init”:

class Hond:
    def __init__(self, naam, geluid):
        self.naam = naam
        self.geluid = geluid

    def maak_geluid(self):
        print(self.naam, "zegt:", self.geluid)

dier1 = Hond("Max", "Waf!")
dier2 = Hond("Sam", "Gaf gaf!")

dier1.maak_geluid() # Max zegt: Waf!
dier2.maak_geluid() # Sam zegt: Gaf gaf!

Oefening 7

Kopieer de code uit oefening 6.

Pas de method __init__ aan, zodat niet enkel de naam, maar ook het bouwjaar en de topsnelheid via deze method ingesteld kunnen worden.

Pas het aanmaken van de objecten model3 en taycan aan, zodat de naam, het bouwjaar en de topsnelheid doorgegeven worden aan de __init__ functie.

Aanpassen properties

Aanpassen van data in class:

dier.naam = "Garfield"
dier.geluid = "Kef!"
dier.maak_geluid() # Garfield zegt: Kef!
dier.naam = "Pluisje"
dier.maak_geluid() # Pluisje zegt: Kef!

Lijsten in classes

Uiteraard kunnen classes ook complexere data bevatten zoals b.v. lijsten:

class Rapport:
    def __init__(self):
        self.punten = []

    def voeg_score_toe(self, score):
        self.punten.append(score)

r = Rapport()
r.voeg_score_toe(7)
r.voeg_score_toe(9)
# r.punten == [7, 9]

Oefening 8

Maak een class Movie. Voeg een property ratings toe. Ratings is een lijst van scores tussen 1 en 5.

Voeg een method add_rating toe, die de rating aan de lijst toevoegt.

Extra methods

class Rapport:
    def __init__(self):
        self.punten = []

    def voeg_score_toe(self, score):
        self.punten.append(score)

    def gemiddelde(self):
        return sum(self.punten)/len(self.punten)

r = Rapport()
r.voeg_score_toe(7)
r.voeg_score_toe(9)
gemiddelde = r.gemiddelde()
print(f"Gemiddelde is: {gemiddelde}")
# Gemiddelde is: 8.0

Oefening 9

Kopieer de code van oefening 8.

Voeg een method som toe, die de som van alle ratings teruggeeft.

Voeg een method gemiddelde toe, die het gemiddelde van alle ratings teruggeeft.

class Rapport:
    def __init__(self):
        self.punten = []

    def voeg_score_toe(self, score):
        self.punten.append(score)

    def gemiddelde(self):
        return sum(self.punten)/len(self.punten)

    def maximum(self):
        return max(self.punten)

r = Rapport()
r.voeg_score_toe(7)
r.voeg_score_toe(9)
gemiddelde = r.gemiddelde()
print(f"Gemiddelde is: {gemiddelde}, max is {r.maximum()}")
# Gemiddelde is: 8.0, max is 9

Oefening 10

Kopieer de code van oefening 9.

Voeg een method minimum toe, die de minimum beoordeling van de film teruggeeft.

Probleemstelling

Welk probleem lossen classes op?

Stel je wil leeftijden van personen bijhouden, en je wil een functie voorzien die de gemiddelde leeftijd berekent. Hoe zou je dit kunnen doen met behulp van lijsten?

Lijst met leeftijden

Je zou een lijst met integers kunnen bijhouden, en een functie die de gemiddelde leeftijd berekent:

leeftijden = [13, 9, 6]

def gemiddelde(l):
    som = 0
    for lt in l:
        som = som + lt
    return som / len(l)

g = gemiddelde(leeftijden)
print(f'gemiddelde leeftijd is: {g}')

Een nadeel is dat leeftijden en gemiddelde niet duidelijk met elkaar verbonden zijn.

Class met leeftijden

Een oplossing hiervoor is het gebruik van een class die zowel de data als de functie die erop werkt bevat:

class Groep:
    leeftijden = [13, 9, 6]

    def gemiddelde(self):
        som = 0
        for lt in self.leeftijden:
            som = som + lt
        return som / len(leeftijden)

groep = Groep()
g = groep.gemiddelde()
print(f'gemiddelde leeftijd is: {g}')

Voorbeelden van classes

Temperatuur op weekdag

class Weerbericht:
    dagen = {
        "maandag": 3,
        "dinsdag": 3,
        "woensdag": 3,
        "donderdag": 10,
        "vrijdag": 9,
        "zaterdag": 5,
        "zondag": 3,
    }
    def warmste_dag(self):
        max_temp = -100
        max_naam = None
        for naam in self.dagen:
            t = self.dagen[naam]
            if t > max_temp:
                max_temp = t
                max_naam = naam
        return max_temp, max_naam
w = Weerbericht()
print(w.warmste_dag())
# toont: (10, donderdag)

BMI

class BMI:
    def __init__(self, gewicht, lengte):
        self.gewicht = gewicht
        self.lengte = lengte
    def bereken(self):
        return self.gewicht / self.lengte ** 2
b = BMI(77.5, 1.835)
print(b.bereken())  
    # toont 23.015984972789166

Kogel class

class Bullet:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.delta_x = 1
        self.delta_y = 0
    def update(self):
        self.x = self.x + self.delta_x
        self.y = self.y + self.delta_y
b = Bullet(100, 40)
b.update()
print(b.x, b.y) # 101, 40
b.update()
print(b.x, b.y) # 102, 40

Vakanties

class Vakanties:
    def __init__(self):
        self.data = {}
    def voeg_toe(self, naam, week):
        self.data[week] = naam
    def toon(self):
        for week in self.data:
            print(week, self.data[week])

v = Vakanties()
v.voeg_toe("Paasvakantie", 10)
v.voeg_toe("Paasvakantie", 11)
v.toon()
# 10 Paasvakantie
# 11 Paasvakantie

Voordelen van classes

Enkele voordelen van classes voor het voorstellen van complexere data zijn:

  • groeperen van code en data
  • encapsulatie (verbergen van implementatiedetails)
  • object oriented design
  • gebruiksgemak/vereenvoudigen van code

Encapsulatie

Stel dat je een Drone wil besturen:

drone = Drone()
drone.start_engine()
drone.move_up(10)
drone.start_camera()
drone.move_forward(20)
drone.turn(45)
drone.move_forward(20)
drone.stop_camera()
drone.land()

Encapsulatie

Enkel de persoon die de Drone class implementeert hoeft te weten hoe de drone werkt.

De gebruiker heeft een makkelijke API om de drone te gebruiken.

Object oriented design

Dikwijls kunnen complexere systemen eenvoudiger gemodelleerd worden, door gebruik te maken van OOD.

Hierbij maken we classes aan die dingen uit de echte wereld voorstellen. Dit vergemakkelijkt het redeneren over de interacties tussen die objecten.

Zie b.v. de Drone class.

Drone en Video API

drone = Drone()
...
video = drone.get_video()
video.save_to_file("mijnopname.mp4")
gps = drone.get_gps_log()
gps.save_to_file("route.gpx")

Minecraft API

mc = minecraft.Minecraft.create("192.168.0.10", 4711)
mc.setBlock(0,0,0,block.STONE.id)
mc.setBlocks(1,1,1,3,3,3,block.WOOD_PLANKS.id)

Minecraft API Python Coding for Minecraft

Vereenvoudigen van code

Veel APIs gebruiken classes om het gebruik en de implementatie ervan te vereenvoudigen.

Dictionaries in dictionaries

Herinner je dit voorbeeld bij gebruik van dictionaries:

kinderen = {
    "Alexander": {"leeftijd": 13, "haarkleur": "bruin"},
    "Zarah": {"leeftijd": 9, "haarkleur": "donkerblond"},
    "Andreas": {"leeftijd": 6, "haarkleur": "blond"},
}
print(kinderen['Alexander']) 
# toont: {'leeftijd': 13, 'haarkleur': 'bruin'}
print(kinderen['Zarah']['haarkleur']) 
# toont: 'donkerblond'

Lijst van objects

De code wordt duidelijk mbv classes:

class Kind:
    def __init__(self, naam, leeftijd, haarkleur):
        self.naam = naam
        self.leeftijd = leeftijd
        self.haarkleur = haarkleur

kinderen = [
    Kind(naam="Alexander", leeftijd=13, haarkleur="bruin"),
    Kind(naam="Zarah", leeftijd=9, haarkleur="donkerblond"),
    Kind(naam="Andreas", leeftijd=6, haarkleur="blond"),
]
print(kinderen[0].leeftijd)  # 13
print(kinderen[1].haarkleur)  # donkerblond

Dictionary van objects

Of voor makkelijkere toegang:

class Kind:
    def __init__(self, naam, leeftijd, haarkleur):
        self.naam = naam
        self.leeftijd = leeftijd
        self.haarkleur = haarkleur

kinderen = {
    "Alexander": Kind(naam="Alexander", leeftijd=13, haarkleur="bruin"),
    "Zarah": Kind(naam="Zarah", leeftijd=9, haarkleur="donkerblond"),
    "Andreas": Kind(naam="Andreas", leeftijd=6, haarkleur="blond"),
}
print(kinderen['Alexander'].leeftijd)  # 13
print(kinderen['Zarah'].haarkleur)  # donkerblond

Lijsten in dictionaries in een dictionary

Herinner je verder ook nog dit voorbeeld:

hobbies = {
    "Alexander": {"leeftijd": 13, "hobbies": ["volleybal", "games", "lezen"]},
    "Zarah": {"leeftijd": 9, "hobbies": ["volleybal", "games", "muziek", "tekenen"]},
    "Andreas": {"leeftijd": 6, "hobbies": ["volleybal", "games"]},
}
print(kinderen['Alexander']['hobbies']) 
# toont: ['volleybal', 'games']
print(kinderen['Zarah']['leeftijd']) 
# toont: 9
print(kinderen['Andreas']['hobbies'][0]) 
# toont: volleybal

Lijsten in classes

class Kind:
    def __init__(self, naam, leeftijd, hobbies):
        self.naam = naam
        self.leeftijd = leeftijd
        self.hobbies = hobbies

kinderen = {
    "Alexander": Kind(naam="Alexander", leeftijd=13, hobbies=["volleybal", "games", "lezen"]),
    "Zarah": Kind(naam="Zarah", leeftijd=9, hobbies=["volleybal", "games", "muziek", "tekenen"]),
    "Andreas": Kind(naam="Andreas", leeftijd=6, hobbies=["volleybal", "games"]),
}
print(kinderen['Andreas'].hobbies)  # ["volleybal", "games"]
print(kinderen['Zarah'].hobbies[0])  # volleybal

Hergebruik code

Stel je wil de speler, vijanden en meteoren kunnen voorstellen in een spel:

class Player:
    def move(self, x, y):
    # ...

class Enemy:
    def move(self, x, y):
    # ...

class Meteor:
    def move(self, x, y):
    # ...

Herbruik code met inheritance

class Entity:
    def move(self, x, y):
    # ...

class Player(Entity):
    pass

class Enemy(Entity):
    pass

class Meteor(Entity):
    pass

Inheritance

class Dier:
    geluid = "?"
    def maak_geluid(self):
        print(self.geluid)

class Hond(Dier):
    geluid = "Woef"

class Kat(Dier):
    geluid = "Miauw"

h = Kat()
h.maak_geluid()
h = Hond()
h.maak_geluid()
// reveal.js plugins