Een Date klasse#
Data
dag
maand
jaar
class Date:
"""Date is a user-defined data structure --
a class that stores and manipulates dates
"""
def __init__(self, day, month, year):
"""The constructor for objects of type Date
"""
self.day = day
self.month = month
self.year = year
def __repr__(self):
"""This method returns a string representation for the
object of type Dat that calls it (named self)
"""
return f"{self.day:02d}/{self.month:02d}/{self.year:04d}"
def is_leap_year(self):
"""Returns True if self, the calling object, is
in a leap year; False otherwise
"""
if self.year % 400 == 0: return True
if self.year % 100 == 0: return False
if self.year % 4 == 0: return True
return False
Een object d#
Stap voor stap#
class Date:
"""Date is a user-defined data structure --
a class that stores and manipulates dates
"""
Dit is het begin van een nieuw type Date aangeven door het class keyword
Let op de dubbele punt, dit betekent dat alles wat volgt ingesprongen moet worden omdat het bij de klasse hoort.
def __init__(self, day, month, year):
"""The constructor for objects of type Date
"""
self.day = day
self.month = month
self.year = year
Dit is de constructor voor Date objecten. Dit is de plek voor waar input data wordt toegekend aan de (data) velden.
d = Date(11, 12, 2013)
De velden zijn de informatie die in elk Date object aanwezig zijn.
def __repr__(self):
"""This method returns a string representation for the
object of type Dat that calls it (named self)
"""
return f"{self.day:02d}/{self.month:02d}/{self.year:04d}"
Dit is de string representatie van Date objecten, het vertelt Python hoe een Date object moet worden geprint.
f"strings"#
Strings formatteren, handig!
klassen = 12
studenten = 25
f"{klassen} klassen met {studenten} studenten is in totaal {klassen * studenten}"
'12 klassen met 25 studenten is in totaal 300'
x = 1
f"{x:02d}"
'01'
02d formatteert de integer x tot een string met een minimale breedte van 2 posities, en “zero-padding” (voorloopmullen) links, indien nodig.
def is_leap_year(self):
"""Returns True if self, the calling object, is
in a leap year; False otherwise
"""
if self.year % 400 == 0:
return True
if self.year % 100 == 0:
return False
if self.year % 4 == 0:
return True
return False
Waarom wordt self gebruikt en niet d?
self#
Is de variabele die de methode aanroept
Misschien heb je al kennisgemaakt met andere programmeertalen en zal je herkennen dat self overeenkomt met wat je kent als this in deze talen (bijvoorbeeld in Java of C++). Guido van Rossum geeft hier antwoord op de vraag waarom niet voor this is gekozen, en legt het uit als een daad van speels verzet.
Het is hier misschien ook van belang om stil te staan bij het feit dat programmeertalen niet zomaar zijn ontstaan, het is mensenwerk en wij bouwen weer voort op het werk van anderen!
wd = Date(11, 12, 2013)
print(wd)
11/12/2013
wd.is_leap_year()
False
d = Date(11, 12, 2020)
print(d)
11/12/2020
d.is_leap_year()
True
Quiz#
De anatomie van een klasse
class Date: # (1)
def __init__(self, day, month, year): # (2)
self.day = day # (3)
self.month = month # (4)
self.year = year # (5)
def __repr__(self): # (6)
return f"{self.day:02d}/{self.month:02d}/{self.year:04d}" # (7)
def is_leap_year(self): # (8)
if self.year % 400 == 0: return True
if self.year % 100 == 0: return False
if self.year % 4 == 0: return True
return False
# (9)
wd = Date(11, 12, 2013) # (10)
ny = Date(1, 1, 2021) # (11)
Probeer de volgende onderdelen aan te wijzen:
classkeywordwaar eindigt de klassedefinitie?
objectdefinities, 2 totaal
methoden, 3 totaal
constructor
datavelden (attributen), 3 totaal
wat print
Date’s?
Oplossing#
classkeyword: (1)waar eindigt de klassedefinitie?: (9)
objectdefinities, 2 totaal: (11, 12)
methoden, 3 totaal: (2, 6, 8)
constructor: (2)
datavelden (attributen), 3 totaal: (3, 4, 5)
wat print
Date’s?: (6)
Methoden en operatoren#
Als alles een object is …
21 < 42
True
"🥚" > "🐔"
True
history = Date(1, 1, 1970)
epoch = Date(1, 1, 1970)
epoch == history
False
Dit werkt niet en dat is jammer! Dit gaan we straks oplossen, je zal Python moeten vertellen dat de operator == (en anderen) betekenis heeft als we objecten van type Date willen gaan vergelijken.
Operatoren#
methode |
operator |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
What’s the diff?#
epoch = Date(1, 1, 1970)
today = Date(6, 12, 2020)
Via een methode
today.diff(epoch)
18602
epoch.diff(today)
-18602
Where’s the dow?#
sm1 = Date(28, 10, 1929)
sm2 = Date(19, 10, 1987)
Gebruikt een named object
sm1.dow()
'Monday'
sm2.dow()
'Monday'
Zonder naam, unnamed!
Date(1,1,1).dow()
'Monday'
Date(1,1,2021).dow()
'Friday'
str("Astronaut wordt snel oud tijdens een reis naar Mars").split()
['Astronaut', 'wordt', 'snel', 'oud', 'tijdens', 'een', 'reis', 'naar', 'Mars']
"Astronaut wordt snel oud tijdens een reis naar Mars".split()
['Astronaut', 'wordt', 'snel', 'oud', 'tijdens', 'een', 'reis', 'naar', 'Mars']
Het == probleem#
wd = Date(11, 12, 2013)
wd
11-12-2013
wd2 = Date(11, 12, 2013)
wd2
11-12-2013
wd == wd2
True
Waarde versus identiteit#
id(wd)
140028148387600
id(wd2)
140028153932176
== zal standaard controleren op identiteit, de geheugenlokatie!
Vergelijken op waarde#
Laten we een eigen test voor gelijkwaardigheid schrijven!
class Date:
def __init__(self, day, month, year):
"""The constructor for objects of type Date
"""
self.day = day
self.month = month
self.year = year
def __repr__(self):
return f"{self.day:02d}/{self.month:02d}/{self.year:04d}"
def is_leap_year(self):
if self.year % 400 == 0: return True
if self.year % 100 == 0: return False
if self.year % 4 == 0: return True
return False
def equals(self, other):
"""Returns True if they represent
the same date; False otherwise
"""
if (
self.year == other.year
and self.month == other.month
and self.day == other.day
):
return True
else:
return False
wd = Date(11, 12, 2013)
wd2 = Date(11, 12, 2013)
wd.equals(wd2)
True
wd2.equals(wd)
True
Maar …
wd == wd2
False
__eq__#
Vertel Python wat wij met gelijkwaardigeid bedoelen!
class Date:
def __init__(self, day, month, year):
"""The constructor for objects of type Date
"""
self.day = day
self.month = month
self.year = year
def __repr__(self):
return f"{self.day:02d}/{self.month:02d}/{self.year:04d}"
def is_leap_year(self):
if self.year % 400 == 0: return True
if self.year % 100 == 0: return False
if self.year % 4 == 0: return True
return False
def __eq__(self, other):
"""Returns True if they represent
the same date; False otherwise
"""
if (
self.year == other.year
and self.month == other.month
and self.day == other.day
):
return True
else:
return False
wd = Date(11, 12, 2013)
wd2 = Date(11, 12, 2013)
wd == wd2
True
Hergebruik#
De methode equals blijven gebruiken maar ook Python vertellen over gelijkwaardigheid (__eq__)?
class Date:
def __init__(self, day, month, year):
"""The constructor for objects of type Date
"""
self.day = day
self.month = month
self.year = year
def __repr__(self):
return f"{self.day:02d}/{self.month:02d}/{self.year:04d}"
def is_leap_year(self):
if self.year % 400 == 0: return True
if self.year % 100 == 0: return False
if self.year % 4 == 0: return True
return False
def equals(self, other):
"""Returns True if they represent
the same date; False otherwise
"""
if (
self.year == other.year
and self.month == other.month
and self.day == other.day
):
return True
else:
return False
def __eq__(self, other):
return self.equals(other)
Operator overloading#
Python duidelijk maken wat een type bedoelt!
__eq__(self, other)definieert de gelijkheid operatator==__ne__(self, otherdefinieert de ongelijkheid operatator!=__lt__(self, other)definieert de kleiner dan operatator<__gt__(self, other)definieert de groter dan operatator>__le__(self, other)definieert de kleiner of gelijk aan operatator<=__ge__(self, other)definieert de groter of gelijk aan operatator>=__add__(self, other)definieert de optelling operatator+__sub__(self, other)definieert de aftrekking operatator-
Morgen en gisteren#
class Date:
...
def tomorrow(self):
"""Moves the self date ahead 1 day
"""
DIM = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
self.day += 1 # first, add 1 to self.day
if ...: # test if we have gone "out of bounds"!
self.month ...
self.day ...
if ...: # then adjust the month and year, but only as needed!
self.year ...
self. month ...
Februari is variabel#
class Date:
...
def tomorrow(self):
"""Moves the self date ahead 1 day
"""
if self.is_leap_year():
fdays = 29
else:
fdays = 28
DIM = [0, 31, fdays, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
self.day += 1 # add 1 to the day
if self.day > DIM[self.month]: # check day
self.month += 1
self.day = 1
if self.month > 12: # check month
self.year += 1
self.month = 1