Een directory doorzoeken¶
Wat staat er allemaal in?¶
Een directory met subdirectories is een boom, en je weet van tevoren niet hoe diep hij is. In het eerste college drukte je daarom met recursie de namen af van alle bestanden in een directory. In dit werkcollege doorzoek je een directory op meer manieren: hoeveel bestanden staan erin, hoeveel ruimte nemen ze in, en staat er ergens een naam die in de terminal problemen geeft? Een naam met een spatie of een apostrof moet je in de terminal tussen aanhalingstekens zetten, en dat vergeet je makkelijk.
De directory¶
Een directory is een dictionary met drie sleutels, net als in het eerste college:
Sleutel |
Waarde |
|---|---|
|
de naam van de directory |
|
een dictionary van bestandsnaam naar grootte in kilobyte |
|
een lijst van subdirectories, elk weer een dictionary in deze vorm |
In de cel hieronder staat de directory documents, met drie subdirectories.
projecten heeft zelf ook weer een subdirectory:
flowchart TD
D["documenten"] --> V(["verslag.docx"])
D --> R(["rooster.pdf"])
D --> P["projecten"]
D --> F["foto's"]
D --> L["leeg"]
P --> PL(["plan.docx"])
P --> LO(["logo.png"])
P --> A["archief"]
A --> N(["notulen.docx"])
A --> S(["setup.exe"])
F --> ST(["strand.jpg"])
F --> B(["berg.jpg"])
EMPTY is een directory zonder bestanden en zonder subdirectories, en PROJECTS
is de subdirectory projecten. Voer de cel eerst uit.
documents = {
"naam": "documenten",
"bestanden": {"verslag.docx": 120, "rooster.pdf": 300},
"directories": [
{
"naam": "projecten",
"bestanden": {"plan.docx": 40, "logo.png": 500},
"directories": [
{
"naam": "archief",
"bestanden": {"notulen.docx": 25, "setup.exe": 900},
"directories": [],
},
],
},
{
"naam": "foto's",
"bestanden": {"strand.jpg": 2000, "berg.jpg": 1800},
"directories": [],
},
{"naam": "leeg", "bestanden": {}, "directories": []},
],
}
EMPTY = {"naam": "leeg", "bestanden": {}, "directories": []}
PROJECTS = documents["directories"][0]
Wat je gaat maken¶
Stap |
Functie |
Doet |
Hoe de recursie loopt |
|---|---|---|---|
1 |
|
het aantal bestanden |
een lus over de subdirectories |
2 |
|
de totale grootte |
|
3 |
|
is een naam veilig in de terminal? |
over een string, vanaf het eind |
4 |
|
alle namen die niet veilig zijn |
een lus over de subdirectories, met stap 3 |
5 |
|
hoeveel niveaus diep de directory is |
een lus over de subdirectories |
Elke functie telt de directory zelf mee én al zijn subdirectories, hoe diep ze ook gaan.
Stap 1: count_files(d)¶
Geeft het aantal bestanden in d en in al zijn subdirectories.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
|
|
Hint
Begin met het aantal bestanden in d zelf: len(d["bestanden"]). Tel daar in
een lus over d["directories"] voor elke subdirectory count_files van die
subdirectory bij op. Het basisgeval is een directory zonder subdirectories: dan
doet de lus niets.
# jouw oplossing
assert count_files(documents) == 8
assert count_files(PROJECTS) == 4
assert count_files(EMPTY) == 0
Stap 2: total_size(d)¶
Geeft de totale grootte in kilobyte van alle bestanden in d en in al zijn
subdirectories.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
|
|
Schrijf het deze keer zonder lus, met sum.
Hint
d["bestanden"].values() geeft de groottes van de bestanden in d, en sum
telt ze op. Voor de subdirectories maak je een list comprehension met voor elke
subdirectory zijn total_size, en die tel je ook op met sum([...]), zoals in
week 2.
# jouw oplossing
assert total_size(documents) == 5685
assert total_size(PROJECTS) == 1465
assert total_size(EMPTY) == 0
Stap 3: safe_name(name)¶
Een naam is veilig als hij alleen letters van a tot en met z en A tot en
met Z bevat, cijfers, en de tekens ., _ en -. Zo’n naam kun je in de
terminal zonder aanhalingstekens typen. Een lege naam telt als veilig: er staat
geen verkeerd teken in.
safe_name(name) geeft True als name veilig is, en anders False. Gebruik
recursie en bekijk de naam vanaf het eind, teken voor teken:
Is de naam leeg, dan geef je
Trueterug.Is het laatste teken geen letter, geen cijfer en niet
.,_of-, dan geef jeFalseterug.Anders roep je de functie opnieuw aan, met de naam zonder het laatste teken, en geef je het resultaat daarvan terug.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
|
|
Hint
Het laatste teken van s is s[-1], en alles ervóór is s[:-1].
ascii_letters is de string met alle letters van a tot en met z en van A
tot en met Z. De cel hieronder zet de goede tekens al in SAFE_CHARS; met in
vraag je of een teken daarin staat. Zodra je een verkeerd teken vindt, weet je
het antwoord.
from string import ascii_letters, digits
SAFE_CHARS = ascii_letters + digits + "._-"
# jouw oplossing
assert safe_name("verslag.docx") == True
assert safe_name("data_2024-01.csv") == True
assert safe_name("foto's") == False
assert safe_name("mijn cv.pdf") == False
assert safe_name("") == True
Stap 4: unsafe_names(d)¶
Geeft een lijst met de namen in d en in al zijn subdirectories die niet veilig
zijn: namen van bestanden en namen van subdirectories. Eerst de bestanden van d
zelf. Daarna, per subdirectory in de volgorde van de lijst, eerst de naam van die
subdirectory, als die niet veilig is, en dan wat erin staat.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
De testcel gebruikt ook een kleine directory WORK met drie onveilige namen.
Hint
De bestanden van d zelf vind je met een list comprehension en safe_name uit
stap 3. Loop daarna de subdirectories langs. Is de naam van een subdirectory
niet veilig, zet hem dan in je lijst, en plak er daarna het resultaat van
unsafe_names voor die subdirectory achter, met +.
# jouw oplossing
WORK = {
"naam": "mijn werk",
"bestanden": {"cv.pdf": 10, "brief 1.docx": 5},
"directories": [
{"naam": "oude versies", "bestanden": {"a:b.txt": 1}, "directories": []},
],
}
assert unsafe_names(documents) == ["foto's"]
assert unsafe_names(WORK) == ["brief 1.docx", "oude versies", "a:b.txt"]
assert unsafe_names(EMPTY) == []
Stap 5: depth(d)¶
Geeft hoeveel niveaus diep d is. Een directory zonder subdirectories is 1 niveau
diep. Een directory met subdirectories is 1 niveau dieper dan zijn diepste
subdirectory.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
|
|
Hint
Onthoud in een lus over de subdirectories de grootste diepte tot nu toe, zoals je
in week 2 het langste woord tot nu toe onthield. Begin bij 0, zodat een directory
zonder subdirectories op 1 + 0 uitkomt.
# jouw oplossing
assert depth(documents) == 3
assert depth(PROJECTS) == 2
assert depth(EMPTY) == 1
Tot slot¶
In documents staan acht bestanden, samen 5685 kilobyte, en één naam is niet
veilig in de terminal: de directory foto's, door de apostrof.
Kijk nog eens naar stap 1, 2, 4 en 5. Elke functie liep de subdirectories langs
en riep zichzelf aan voor elke subdirectory, in een lus of in een list
comprehension. Nergens stond een if voor het basisgeval: een directory zonder
subdirectories heeft een lege lijst, en dan volgt er geen recursieve aanroep.
Stap 3 werkte anders: daar was het kleinere probleem een kortere string, en
dan heb je wel een if nodig om te stoppen.