More on Lists

Deze pagina bevat uitvoerbare code.

Data

  • string

  • integer / float

  • list

2D arrays zijn lijsten van lijsten, LoL’s

Lists

Lists zijn containers, ze bevatten verwijzingen naar data

my_list = [5, 42, "hi"]

List references

Indentiteit

help(id)
Help on built-in function id in module builtins:

id(obj, /)
    Return the identity of an object.

    This is guaranteed to be unique among simultaneously existing objects.
    (CPython uses the object's memory address.)

Het geheugenadres van my_list

id(my_list)
140133778384640

De geheugenadressen van de elementen van L

id(my_list[0])  # 5
140133910222792
id(my_list[1])  # 42
140133910223976
id(my_list[2])  # "hi"
140133910266656

Waarde en identiteit

a = "Astronaut wordt snel oud tijdens een reis naar Mars"
b = "Astronaut wordt snel oud tijdens een reis naar Mars"
a == b
True
a is b
False
id(a)
140133780070800
id(b)
140133780071472

Verwijzingen

by reference

Lists bevatten verwijzingen naar geheugenadressen, niet de waarden zelf!

by value

Getallen en strings verwijzen naar de waarde

Mutabiliteit

mutable (veranderlijk)

Lists kunnen worden aangepast

immutable (onveranderlijk)

Getallen en strings kunnen niet worden aangepast

Mutable

Lists

my_list = [11, 21]
id(my_list)
140133778435776
my_list[0] = 42
id(my_list)
140133778435776
my_list
[42, 21]

Immutable

Strings en getallen

string = "hallo "
id(string)
140133778474496
string += "wereld"
id(string)
140133778465904
number = 10
id(number)
140133910222952
number += 1
id(number)
140133910222984

Data die zelf iets doet

Tot nu toe deed jij iets met data. De functie staat vooraan en de data gaat erin:

len(my_list)
sum(my_list)
my_list + [9]

Vanaf nu zie je het ook andersom:

my_list.append(9)

Hier staat de lijst vooraan en de handeling erachter. Je vraagt niet aan een functie om iets met de lijst te doen; je vraagt het aan de lijst zelf.

Zo’n handeling die bij de data zelf hoort heet een methode. Je schrijft hem met een punt: eerst het ding, dan de punt, dan wat het moet doen.

my_list = [11, 21]
my_list.append(42)
my_list
[11, 21, 42]

Let op wat er niet gebeurt: er komt geen nieuwe lijst uit. append geeft niets terug, hij verandert de lijst die er al was.

my_list = [11, 21]
resultaat = my_list.append(42)
print(resultaat)
None

None, dus. Dit is dezelfde mutatie als hierboven met my_list[0] = 42, alleen anders opgeschreven.

Daarmee heb je twee manieren om er een element bij te zetten:

Wat er gebeurt

Schrijfwijze

Er komt een nieuwe lijst

my_list = my_list + [9]

De lijst zelf verandert

my_list.append(9)

Allebei goed. Wanneer het verschil uitmaakt zie je zo meteen.

Neem dit voorlopig aan

Waaróm data zelf iets kan doen, leer je in Programmeren 2. Daar maak je ze zelf: data met handelingen eraan vast, en zoiets heet een object.

Voor nu is het een schrijfwijze die je moet herkennen en kunnen gebruiken. Je komt hem overal tegen, want vrijwel alles in Python werkt zo, en dat is ook de reden dat je hem nu al nodig hebt.

Functies?

by copy

Functies ontvangen parameters als kopie

def fav(x):
    print("fav VOOR: x is", id(x), "en heeft de waarde x", x)
    x = "Pizza quattro formaggi"
    print("fav NA: x is", id(x), "en heeft de waarde x", x)
    
def main():
    y = "Pizza salami ananas"  # bah
    print("main VOOR: y is", id(y), "en heeft de waarde", y)
    fav(y)
    print("main NA: y is", id(y), "en heeft de waarde", y)
main()
main VOOR: y is 140133778504688 en heeft de waarde Pizza salami ananas
fav VOOR: x is 140133778504688 en heeft de waarde x Pizza salami ananas
fav NA: x is 140133778501488 en heeft de waarde x Pizza quattro formaggi
main NA: y is 140133778504688 en heeft de waarde Pizza salami ananas

Shallow versus deep copy

De ene kopie is de andere niet!

Shallow copy

Assignment statements in Python do not copy objects, they create bindings between a target and an object.

https://docs.python.org/3/library/copy.html

Wat create bindings hier betekent laten we in het midden, duidelijk is dat geen kopie wordt gecreëerd maar iets van een verwijzing die later eenvoudig te verbreken valt.

x = "regen"
y = x
x is y
True
y = "zonneschijn"
x is y
False
print("Na", x, "komt", y)
Na regen komt zonneschijn

Lists en shallow copy

my_list = [5, 42, "hi"]
your_list = my_list
your_list[0] = 60
your_list[0]
60
my_list[0]
60

Lists zijn mutable en de shadow copy M verwijst nog steeds naar L en een aanpassing van M zal niet leiden tot een nieuwe list (zoals je wel zag gebeuren bij strings en integers).

Deep copy

Deep copy is alleen relevant voor containertypes als lists!

A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

https://docs.python.org/3/library/copy.html

Een deep copy creëert een nieuwe container, eventuele wijzigingen zijn vervolgens alleen van toepassing op de kopie, niet het origineel.

from copy import deepcopy
my_list = [5, 42, "hi"]
your_list = deepcopy(my_list)
your_list is my_list
False
your_list[0] = 60

Wat is de waarde van my_list[0]?

my_list[0]
5

Slicing is ook diep!

my_list = [5, 42, "hi"]
your_list = my_list[:]
your_list[0] = 60
my_list[0]
5

Quiz

Vraag 1

def conform1(fav):
    fav = 42
    return fav

def main1():
    fav = 7
    conform1(fav)
    print(fav)

Wat wordt geprint voor fav in de functie main1?

Antwoord

main1()
7

Vraag 2

def conform2(lst):
    lst = [42, 42]
    return lst

def main2():
    lst = [7, 11]
    conform2(lst)
    print(lst)

Wat wordt geprint voor lst in de functie main2?

Antwoord

main2()
[7, 11]

Vraag 3

def conform3(lst):
    lst[0] = 42
    lst[1] = 42
    
def main3():
    lst = [7, 11]
    conform3(lst)
    print(lst)

Wat wordt geprint voor lst in de functie main3?

Antwoord

main3()
[42, 42]

Dictionaries

Dictionaries zijn willekeurige containers

d = {47: 2, 42: 1}

Elementen (of waarden) worden opgehaald met een sleutel op een willekeurige positie

d[47] == 2
d[42] == 1

Goed nieuws, sleutels kunnen ook andere typen dan int zijn!

Van Dale

Een bekende structuur

  • woord ⟶ verklaring

  • naam ⟶ telefoonnummer

  • afkorting ⟶ betekenis

  • dier ⟶ jaren Chinese dierenriem

Chinese dierenriem

Dictionaries zijn in

zodiac_years = {
    "rabbit": [1999, 1987, 1975],
    "ox": [1997, 1985, 1973],
    "dragon": [2000, 1998]
}

De sleutels zijn hier strings en de bijbehorende waarden zijn lists. Dit voorbeeld gaat over de jaren per dier in de Chinese dierenriem, zie het Wikipedia artikel voor alle dieren en jaren!

Is "dragon" een sleutel in zodiac_years?

"dragon" in zodiac_years
True

Is 1969 een waarde in zodiac_years["dragon"]?

1969 in zodiac_years["dragon"]
False

Woorden tellen

list_of_words = ["spam", "spam", "taart", "spam"]
word_count = {}
for word in list_of_words:
    if word not in word_count:
        word_count[word] = 1
    else:
        word_count[word] += 1
word_count
{'spam': 3, 'taart': 1}

Model genereren

Gegegeven de volgende text:

text = "Ik wil taarten en 42 en spam. Ik krijg toch spam en taarten voor de vakantie? Ik wil 42 taarten!"
list_of_words = text.split()
word_count = {}

for word in list_of_words:
    if word not in word_count:
        word_count[word] = 1
    else:
        word_count[word] += 1
        
print(f"There are {len(word_count)} DISTINCT words")  # expressions in f-strings!

word_count
There are 13 DISTINCT words
{'Ik': 3,
 'wil': 2,
 'taarten': 2,
 'en': 3,
 '42': 2,
 'spam.': 1,
 'krijg': 1,
 'toch': 1,
 'spam': 1,
 'voor': 1,
 'de': 1,
 'vakantie?': 1,
 'taarten!': 1}

Met een paar aanpassingen van het programma dat worden telt, is het mogelijk om een eenvoudige taalgenerator te maken.

In plaats van het tellen van woorden zal worden gekeken welk woord na een voorgaand woord wordt gebruikt.

%run assets/markov.py
words_follow = create_dictionary(text)
words_follow
{'$': ['Ik', 'Ik', 'Ik'],
 'Ik': ['wil', 'krijg', 'wil'],
 'wil': ['taarten', '42'],
 'taarten': ['en', 'voor'],
 'en': ['42', 'spam.', 'taarten'],
 '42': ['en', 'taarten!'],
 'krijg': ['toch'],
 'toch': ['spam'],
 'spam': ['en'],
 'voor': ['de'],
 'de': ['vakantie?']}

Het stappenplan voor het maken van een taalmodel is als volgt:

  1. begin met het vorige woord previous_word als “$”

  2. voor elk volgend woord in de lijst van woorden voeg het toe aan …

  3. maak previous_word gelijk aan new_word

    • behalve als new_word[-1] punctuatie is, maak previous_word dan gelijk aan …

Tekst genereren

Een $ staat voor het begin van een zin. Dit model heeft voor elk woord een lijst van woorden waar uit gekozen kan worden. Als we dit random doen, wordt er een random tekst genereerd.

generate_text(words_follow, 42)
'Ik wil taarten en 42 en spam. Ik krijg toch spam en taarten en 42 en 42 en taarten en taarten voor de vakantie? Ik wil 42 en 42 en taarten voor de vakantie? Ik wil 42 taarten! Ik krijg toch spam '

Als we een veel grotere tekst gebruiken om onze database mee te vullen, kunnen we betere teksten generen.

Het stappenplan voor het genereren van tekst is als volgt:

  1. begin met previous_word als de “$” string

  2. kies random een new_word dat volgt op previous_word en voeg het toe aan …

  3. maak previous_word gelijk aan new_word

    • behalve als new_word[-1] punctuatie is, maak dan previous_word gelijk aan …