Skip to content

Commit 7579192

Browse files
committed
theory
0 parents  commit 7579192

File tree

15 files changed

+821
-0
lines changed

15 files changed

+821
-0
lines changed

IO/index.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# File Input & Output
2+
In Python kan je op verschillende manieren werken met (data)bestanden. Maar je zal ongetwijfeld de `open()` functie nodig hebben. Deze functie opent een bestand voor lezen of voor schrijven. Zodra je dat hebt gedaan kun je uit het bestand lezen, en eventueel naar het bestand schrijven. Zodra je klaar bent met het bestand moet je dit weer sluiten d.m.v. `close()`. Omdat je dat laatste nog weleens wilt vergeten heeft Python een speciaal construct in het leven geroepen: de `with` statement. Deze statement sluit het geopende bestand nadat je er klaar mee bent meteen, automatisch en voor niks. Zo kan je dit nooit vergeten! Het ziet er als volgt uit:
3+
4+
with open("myfile.txt") as f:
5+
# do magic
6+
7+
Laten we beginnen met wat voorbeelden. Zo kan je bijvoorbeeld een regel uit jouw bestand lezen en printen naar het scherm:
8+
9+
with open("myfile.txt") as f:
10+
line = f.readline()
11+
print(line)
12+
13+
Wil je het woord `"hello"` wegschrijven dan doe je dat zo:
14+
15+
with open("myotherfile.txt", "w") as f:
16+
f.write("hello")
17+
18+
Let erop dat je een extra argument meegeeft aan open, in dit geval de letter `"w"` van write. Standaard opent Python een bestand in leesmodus en kun je er dus niet naar schrijven. Allebei tegelijk nu, inlezen vanuit de ene bestand, en schrijven naar de ander:
19+
20+
with open("inputfile.txt") as fIn, open("outputfile.txt", "w") as fOut:
21+
line = fIn.readline()
22+
fOut.write(line)
23+
24+
Er zijn verschillende manieren om meerdere regels uit een bestand te lezen. De makkelijkste en gebruikelijkste is d.m.v. een for-loop. Deze stopt automatisch als je het einde van het bestand bereikt. Zo print je bijvoorbeeld de gehele inhoud van het bestand naar je scherm:
25+
26+
with open("inputfile.txt") as f:
27+
for line in f:
28+
print(line)
29+
30+
Wil je liever handmatig lezen omdat je bijvoorbeeld meerdere regels in één keer nodig hebt. Dan kan dat ook d.m.v. een while-loop. De code hieronder print ook de gehele inhoud van het bestand naar je scherm:
31+
32+
with open("inputfile.txt") as f:
33+
while True:
34+
line = f.readline()
35+
if not line:
36+
break
37+
print(line)
38+
39+
Ziet er wel gelijk een stuk ingewikkelder uit. Je moet namelijk elke keer kijken of je niet al aan het einde van je bestand bent. De if-statement handelt dat geval af, ofwel als er geen regel is ingelezen stop dan met de loop. In plaats van regel voor regel inlezen kan je ook in één keer het hele bestand inlezen. Dit kan, maar is niet aan te raden met hele grote databestanden (in de meerdere gigabytes). De code hieronder leest in één keer het hele bestand in en print het regel voor regel naar het scherm.
40+
41+
with open("inputfile.txt") as f:
42+
lines = f.readlines()
43+
for line in lines:
44+
print(line)

classes/index.md

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Classes
2+
3+
Naarmate programma's groter worden neemt de wens voor betere data abstracties toe. Het is makkelijker om na te denken en te praten over concepten in jouw programma zonder het te hebben over de technische details. Zo maakt het niet uit of je een sudoku puzzel representeert met een string, tuple of een list. Wat voor jou en je collega's / medestudenten relevant is is wat je ermee kan.
4+
5+
Object oriented programming geeft jouw het gereedschap om data abstracties te creëeren in jouw programma. Zo kan je data en methodes combineren / bundelen onder één kap. Daarbij krijg je de kans om de interactie te bepalen. Wat kan jouw abstractie en hoe ga je ermee om?
6+
7+
In Python heb je classes, dat zijn beschrijvingen van objecten. Hierin definieer je precies wat een object kan en ook hoe. Zie het als een blauwdruk, waarbij bij elk detail meteen de uitwerking staat. Zie hieronder de class `Coordinate`.
8+
9+
10+
class Coordinate:
11+
def __init__(self, x, y):
12+
self.x = x
13+
self.y = y
14+
15+
def distance_to(self, other):
16+
return (abs(self.x - other.x)**2 + abs(self.y - other.y)**2)** 0.5
17+
18+
19+
Bovenstaande class introduceert een nieuw type in ons programma namelijk `Coordinate`. Vanaf nu kan je instanties ofwel objecten van het type `Coordinate` maken. Dit doe je als volgt:
20+
21+
22+
coord1 = Coordinate(3, 5)
23+
coord2 = Coordinate(2, 8)
24+
25+
26+
Hier gebeurt het volgende, zodra je de class `Coordinate` aanroept, roept Python de methode `__init__` voor je aan. Init is kort voor initialize, ofwel de methode die wordt aangeroepen bij het initialiseren van een object. De methode `__init__` accepteert hier 3 argumenten. Dit is waar Python een beetje hacky wordt, want het eerst argument `self` vult Python voor je in. Dat is een referentie/verwijzing naar het object. Dat dit argument `self` heet is conventie, we doen het allemaal, maar het hoeft niet. Naast `self` staan nog 2 argumenten, hier `x` en `y`. Logisch ook want we hebben het over een coordinaat! Omdat er 2 argumenten naast `self` zijn moet je deze bij het aanmaken van een `Coordinate` ook meegeven, zo wordt er voor `coord1` de waarde `3` meegegeven voor `x` en `5` voor `y`. Op de regels daarna wordt er nieuwe zogenaamde "instance variables" aangemaakt voor het `Coordinate`, `.x` en `.y` respectievelijk. Daaraan worden de waardes `3` en `5` bij `coord1` toegekend.
27+
28+
## Instance variables
29+
30+
Een instance variable is een variabele die enkel beschikbaar is voor die instantie. Zo hebben `coord1` en `coord2` allebei een instance variable genaamd `.x`, maar deze heeft een hele andere waarde. Test het volgende maar eens:
31+
32+
33+
print(coord1.x)
34+
print(coord2.x)
35+
36+
37+
Dat is ook hoe je bij de waardes van de `instance variable`s komt. Namelijk het object (de instance) en vervolgens een `.` en daarna de variabele naam.
38+
39+
Dit is super handig, want nu kunnen we een naam toekennen aan deze variabelen. Zo hoef je het niet meer te hebben over plek 0 en 1, maar kan je nadenken over een x en y coordinaat. Neemt weer wat cognitive load af en zo kan je collega niet de fout maken om `x` en `y` door elkaar te halen!
40+
41+
## Methods
42+
43+
Naast variabelen kunnen we ook functies toe kennen aan een class. Deze noemen we dan net even anders: `methods`. Dat zijn simpelweg functies behorende bij een class. Beide namen worden vaak door elkaar gebruikt!
44+
45+
Hiermee kunnen we niet alleen data groeperen onder één kap, maar ook de bijbehorende operaties en code. De class `Coordinate` kent dan ook één method genaamd `distance_to`. Elke method accepteert in Python altijd als eerst een verwijzing naar het object, die ene instantie waarvan de afstand willen weten, weer per conventie `self` genoemd. Daarnaast kan een method 0 of meer argumenten accepteren. `distance_to` accepteert er één meer, namelijk `other`. Klinkt logisch, we hebben het coordinaat `self` en een `other`.
46+
47+
Wat de method `distance_to` vervolgens doet is de afstand tussen de twee coordinaten berekenen. Daarom ook de naam `distance_to`! Je kan de method als volgt gebruiken:
48+
49+
50+
coord1.distance_to(coord2)
51+
52+
53+
Voor Python is dit equivalent aan:
54+
55+
56+
Coordinate.distance_to(coord1, coord2)
57+
58+
59+
Het eerste is enkel minder typen!
60+
61+
## Class variables
62+
63+
Daarnaast bestaan er class variables. Deze verschillen niet per object/instantie van een class, maar bestaan één keer voor alle objecten van een class.
64+
65+
66+
class Request:
67+
count = 0
68+
69+
def __init__(self, type, message):
70+
# do something with type and message
71+
Request.count += 1
72+
73+
Zo heeft de class `Request` hierboven een class variable `count`. Deze wordt elke keer opgehoogd bij het initialiseren van een instantie van Request.
74+
75+
76+
print(Request.count) # prints 0
77+
request1 = Request("get", "homepage")
78+
print(Request.count) # prints 1
79+
print(request1.count) # prints 1
80+
request2 = Request("get", "accountpage")
81+
print(Request.count) # prints 2
82+
print(request1.count) # prints 2
83+
print(request2.count) # prints 2
84+
85+
86+
Je kan via de class `Request` zelf bij de class variabele, maar ook via elke instantie/object van de class, in het voorbeeld hierboven `request1` en `request2`. Het zijn verschillende wegen naar Rome, maar de uitkomst is hetzelfde, de waarde van de class variabele `count`!
87+
88+
89+
## Hidden methods
90+
91+
Python is gebouwd op Object Oriented Programming. Bijna alle constructies in de taal vertalen naar een method call. Bijvoorbeeld:
92+
93+
94+
a = 1
95+
b = 3
96+
a = a + b
97+
print(a)
98+
99+
100+
Is eigenlijk het volgende:
101+
102+
103+
a = 1
104+
b = 3
105+
a = a.__add__(b)
106+
print(a.__str__())
107+
108+
109+
Wat Python hiermee bereikt is dat jij als programmeur zelf kan bepalen wat er moet gebeuren als je bijvoorbeeld twee `Coordinate`s bij elkaar optelt, of hoe een `Coordinate` eruit moet zien als jij deze uitprint. Je moet alleen even opzoeken wat je precies moet implementeren en vervolgens rest de taak om het te implemteren. Bijvoorbeeld als volgt:
110+
111+
112+
class Coordinate:
113+
def __init__(self, x, y):
114+
self.x = x
115+
self.y = y
116+
117+
def __str__(self):
118+
return f"coord: {x},{y}"
119+
120+
def __add__(self, other):
121+
return Coordinate(self.x + other.x, self.y + other.y)
122+
123+
def distance_to(self, other):
124+
return (abs(self.x - other.x)**2 + abs(self.y - other.y)**2)** 0.5
125+
126+
Met zowel de `__str__` en `__add__` methodes geïmplementeerd kunnen we vervolgens het `+` teken en de functie `print()` gebruiken voor instanties/objecten van `Coordinate`.
127+
128+
129+
coord1 = Coordinate(3, 5)
130+
coord2 = Coordinate(2, 8)
131+
print(coord1) # print coord: 3,5
132+
print(coord2) # print coord 2,8
133+
print(coord1 + coord2) # print coord 5,13

dicts/index.md

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Dict
2+
3+
## Definitie
4+
Een in Python ingebouwde datastructuur is een `dict`, kort voor dictionary. Een dict is een muteerbare verzameling van key-value paren. Laten we even goed kijken naar wat dat precies inhoud.
5+
6+
Allereerst, een dict is een verzameling van key-value paren. Op basis van een key kun je een value ophalen. Klinkt misschien een beetje cryptisch, maar kijk even naar het volgende voorbeeld:
7+
8+
some_dict = {1:3, "hello":"world"}
9+
print(some_dict[1])
10+
print(some_dict["hello"])
11+
12+
`some_dict` in het stukje code hierboven is zo'n dict. Hiervoor gebruik je in Python de accolades (`{}`), en de `:` om keys en values te scheiden. Links van de `:` staat de key, recht de value. Op basis van de key kan je de bijbehorende value ophalen. Zo print het bovenstaande stukje code eerst `3` en vervolgens `"world"`.
13+
14+
Een dict kent de volgende eigenschappen:
15+
- Een dict is muteerbaar, dat betekent dat je de verzameling kan aanpassen (muteren). Ofwel, we kunnen er elementen uit verwijderen en aan toevoegen.
16+
- Een dict bestaat uit key-value paren, op basis van een key haal je de bijbehorende value op. Dat betekent dat keys dus uniek zijn, je kan niet twee keer dezelfde key in een dictionary hebben. Daar zorgt de datastructuur zelf voor.
17+
18+
## Waarvoor / wanneer
19+
Ben je geïntereseerd in het opslaan van data op een relationele manier (bijv. voornaam -> achternaam), dan is een dict een heel handige datastructuur!
20+
21+
Ook zijn dicts ontzettend snel O(1) in verschillende operaties zoals:
22+
23+
- Het toevoegen van elementen (voeg key-value paar K:V aan dict D toe)
24+
- Het updaten (vervangen) van values (update value V van key K uit dict D)
25+
- Het verwijderen van elementen (verwijder key-value paar K:V uit dict D)
26+
- Het opzoeken van keys (zit key K in dict D?)
27+
28+
## Code
29+
Een dict maak je zo aan:
30+
31+
numbers = {1:3, 4:5}
32+
print(numbers)
33+
34+
Hiervoor gebruik je in Python de accolades (`{}`), en de `:` om keys en values te scheiden. Links van de `:` staat de key, recht de value. Op basis van de key kan je de bijbehorende value ophalen. Wil je een lege dict aanmaken, dan kan dat zo:
35+
36+
numbers = {}
37+
numbers[1] = 3
38+
numbers[4] = 5
39+
print(numbers)
40+
41+
Bovenstaande stukje code maakt een lege dict en voegt de key-value paren 1:3 en 4:5 toe. Behalve toevoegen kunnen we natuurlijk ook verwijderen:
42+
43+
numbers = {1:3, 4:5}
44+
del numbers[1]
45+
print(numbers)
46+
47+
Zit een key-value paar al in de dict, dan kun je de value ook updaten of vervangen. Bijvoorbeeld:
48+
49+
numbers = {1:3, 4:5}
50+
numbers[1] = 2
51+
numbers[4] += 1
52+
print(numbers)
53+
54+
Tot slot, alle keys in een dict zijn uniek. Altijd. Je kan geen dubbele keys hebben. Kijk maar:
55+
56+
numbers = {}
57+
numbers[1] = 2
58+
numbers[1] = 3
59+
print(numbers)
60+
61+
De syntax voor het toevoegen van keys is hetzelfde als die voor het updaten van keys. Zou je dus dezelfde key willen toevoegen, dan update je eigenlijk de oude value!

list comprehensions/index.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Comprehensions
2+
3+
In Python heb je comprehensions om op één regel een collectie aan te maken. We gaan binnen deze cursus enkel focus leggen op `list` comprehensions. Houd wel in je achterhoofd dat er meer bestaan!
4+
5+
Een comprehension is eigenlijk niet meer dan een for-loop op een regel om een collectie te maken. In het geval van een `list` comprehension maken we dus op één regel een `list`. Dit ziet er als volgt uit:
6+
7+
some_list = [i * 2 for i in range(10)]
8+
9+
Bovenstaande creëert een lijst met alle tweevouden van 0 t/m 18, oftewel `[0,2,4,6,8,10,12,14,16,18]`. Je leest dit als volgt: zet voor elke `i` in `range(10)` `i*2` neer in de lijst. Het is effectief een korte manier om het volgende voor elkaar te krijgen:
10+
11+
some_list = []
12+
for i in range(10):
13+
some_list.append(i * 2)
14+
15+
Behalve enkel iets met de waarde i doen kan je list comprehensions ook heel handig gebruiken om de uitkomsten van meerdere functie aanroepen te verzamelen:
16+
17+
import random
18+
random_numbers = [random.random() for i in range(100)]
19+
20+
Maar er is meer, zo kan je bijvoorbeeld ook een conditie verwerken in een list comprehension. De volgende twee stukken code zijn dan ook functioneel equivalent:
21+
22+
some_list = [i * 2 for i in range(10) if i % 2 == 0]
23+
24+
some_list = []
25+
for i in range(10):
26+
if i % 2 == 0
27+
some_list.append(i * 2)
28+
29+
Behalve nieuwe lijsten aanmaken kan je list comprehensions ook gebruiken om operaties op collecties uit te voeren. Dan loopen we niet door `range(10)` maar de bestaande collectie. Bijvoorbeeld:
30+
31+
numbers = [1,4,9,3,2,5,10,6]
32+
strings = [str(number) for number in numbers]
33+
34+
Ook kan je list comprehensions gebruiken om te filteren, door middel van zo'n if statement. Bijvoorbeeld:
35+
36+
numbers = [1,4,9,3,2,5,10,6]
37+
even_numbers = [number for number in numbers if number % 2 == 0]
38+
39+
List comprehensions kunnen dus heel handig zijn om met heel weinig code, veel voor elkaar te krijgen. Vaak zijn ze ook nog leesbaarder dan een grote for-loop! Als je een for-loop schrijft, denk dan eens of je dit simpeler kan maken met een list comprehension.
40+
41+
Let wel, je kan heel ver gaan. Zo kun je multidimensionele list comprehensions schrijven met allerlei condities erin. Op een gegeven moment moet je jezelf afvragen of het nog echt overzichtelijker is dan een gewone for-loop!
42+
43+
dont_do_this_at_home = [[a * b for a in range(10) for b in range(5) if a > b] for i in range(3)]

lists/index.md

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# List
2+
3+
## Definitie
4+
Python heeft een aantal datastructuren in de taal ingebouwd, één zo'n structuur is een `list`. Een list is een muteerbare geordende verzameling. Laten we even goed kijken naar wat dat precies inhoud.
5+
6+
- Een list is muteerbaar, dat betekent dat je de verzameling kan aanpassen (muteren). Ofwel, we kunnen er elementen uit verwijderen en aan toevoegen.
7+
- Een list is geordend, dat betekent dat de elementen in de verzameling een volgorde kennen. De volgorde waarin je elementen toevoegt aan de list maakt dus uit.
8+
9+
## Waarvoor / wanneer
10+
Een list kun je heel goed gebruiken op plekken waar je arrays zou gebruiken in C. Ze zijn ook heel vergelijkbaar! Een verschil is wel, lists kun je uitbreiden. Je hoeft niet vooraf een grootte op te geven.
11+
12+
Lists zijn snel O(1) in verschillende operaties zoals:
13+
14+
- Het ophalen van elementen op een index (welk element staat er op plek 3?)
15+
- Het toevoegen van elementen aan het einde van de lijst
16+
- Het verwijderen van het element op de laatste index
17+
- Het updaten van een element op een index
18+
19+
En langzamer O(n) in andere operaties:
20+
21+
- Het opzoeken van een element (staat element E in list L?)
22+
- Het verwijderen van elementen op basis van index (behalve de laatste index)
23+
- Het toevoegen van een element op een andere index dan aan het einde van de list
24+
25+
## Code
26+
Een list maak je zo aan:
27+
28+
numbers = [1,3]
29+
print(numbers)
30+
31+
Hiervoor gebruik je in Python de blokhaken (`[]`). Wil je een lege list aanmaken, dan kan dat zo:
32+
33+
numbers = []
34+
numbers.append(1)
35+
numbers.append(3)
36+
print(numbers)
37+
38+
Bovenstaande stukje code maakt een lege list en voegt de elementen 1 en 3 aan het einde van de list toe toe. Behalve toevoegen kunnen we natuurlijk ook verwijderen:
39+
40+
numbers = [1,3]
41+
del numbers[0]
42+
print(numbers)
43+
44+
Updaten van elementen in de list gaat als volgt:
45+
46+
numbers = [1,3]
47+
numbers[0] = 2
48+
numbers[1] = 4
49+
print(numbers)
50+
51+
Een list is geordend. Dat betekent dat de datastructuur een volgorde kent. Het maakt dus uit waar je elementen toevoegt, en in welke volgorde.
52+
53+
numbers = []
54+
numbers.append(1)
55+
numbers.append(3)
56+
print(numbers)
57+
numbers = []
58+
numbers.append(3)
59+
numbers.append(1)
60+
print(numbers)
61+
62+
## Omgekeerde indexen
63+
In Python kan je heel makkelijk het laatste element van een geordende verzameling zoals een list pakken, door gebruik te maken van het `-` teken. Dan tel je van achter naar voren, kijk maar:
64+
65+
numbers = [10,20,30,40,50,60]
66+
print(numbers[-1]) # print 60
67+
print(numbers[-2]) # print 50
68+
print(numbers[-3]) # print 40
69+
70+
## Slicing
71+
Python kent een simpele syntax om een deel van een list (of andere geordende verzameling) te pakken, te "slicen":
72+
73+
numbers = [10,20,30,40,50,60]
74+
print(numbers[1:4]) # prints [20,30,40]
75+
print(numbers[:4]) # prints [10,20,30,40]
76+
print(numbers[1:]) # prints [20,30,40,50,60]
77+
print(numbers[:]) # prints [10,20,30,40,50,60]
78+
print(numbers[:-1]) # prints [10,20,30,40,50]
79+
print(numbers[-3:]) # prints [40,50,60]
80+
print(numbers[-3:-1]) # prints [50,60]
81+
82+
`numbers[:]` lijkt misschien een beetje flauw. Het is echter een simpel trucje om een kopie te maken van de lijst. Ontzettend handig als je wilt dat jouw originele exemplaar niet veranderd!
83+
84+
## Extended slicing
85+
Python kent ook een stapsgrootte bij slicing, deze kan ook negatief zijn. Ontzettend handig om bijvoorbeeld een geordende verzameling zoals een list om te draaien:
86+
87+
numbers = [10,20,30,40,50,60]
88+
print(numbers[1:5:2]) # prints [20,40]
89+
print(numbers[::2]) # prints [10,30,50]
90+
print(numbers[1::2]) # prints [20,40,60]
91+
print(numbers[::-1]) # prints [60,50,40,30,20,10]

0 commit comments

Comments
 (0)