Les og utforsk
Denne uken skal vi lese data fra en fil. Se på Section 7.2 og 7.2.1 i Python Tutorial: https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files
Les også gjennom kapittelet om Exceptions fra Pythons egen tutorial: https://docs.python.org/3/tutorial/errors.html
Lese fra en fil
Python kommer med innebygde funksjoner som gjør det enkelt å skrive og lese filer. I dette eksemplet skal vi prøve å lese og skrive til en fil. Du må laste ned tekstfilen: timemachine.txt og ha dem i samme mappe på datamaskinen din.
Hvis du støter på en FileNotFoundError, kan du sjekke ut Hjelp-filen-blir-ikke-funnet -delen for mulige løsninger.
Her er tre måter å få informasjon fra en fil i Python:
- Den vanligste måten er bruk av en for-løkke:
with open("timemachine.txt", encoding="utf8") as f:
for line in f:
line = line.strip() # removes spaces before and after string
l = len(line)
print("The line is", l, "long, and ends with")
print(">>>", line[-20:], "<<<")
- Bruk av readlines()-metoden:
with open("timemachine.txt", encoding="utf8") as f:
all_lines = f.readlines()
print(len(all_lines), "lines in the text")
- Bruk av read()-metoden:
with open("timemachine.txt", encoding="utf8") as f:
all_lines = f.read()
print("vvvvvvv")
print(all_lines)
print("^^^^^^^")
Hva er forskjellen mellom for line in f
, f.readlines()
og f.read()
for å lese fra filen?
Skrive til en fil
with open("numbers.txt", "w", encoding="utf8") as f:
for i in range(100):
f.write(f"{i} is a nice number\n")
# now open the file numbers.txt in your editor and see what's inside
# we can also use .writelines() to write to the file
with open("numbers2.txt", "w", encoding="utf8") as f:
# here we use a list comprehension, could also make this a regular loop
lines = [f"{i} is a nice number\n" for i in range(100)]
f.writelines(lines)
# open the file numbers2.txt in your editor and see what's inside
# write other files!
Hjelp, filen blir ikke funnet
Når du kjører et Python-program, kjører programmet «i» en mappe som kalles current working directory (cwd). Du kan se hvilken mappe dette er med koden:
import os
cwd = os.getcwd()
print(cwd)
Denne mappen blir bestemt av terminalen som starter python. F. eks. hvis du bruker VSCode for å starte python, vil terminalen være i den samme mappen som VSCode er åpnet i (den som er nevnt med STORBOKSTAVER i venstre menyen). Mappen terminaler er i har altså ikke noen sammenheng med hvilken mappe filen som kjøres ligger i.
Når python får beskjed om å åpne en fil, vil den tolke filstien som blir oppgitt relativt til cwd. For eksempel, hvis filstien er kun et filnavn, antas det at filen ligger i cwd.
La oss si at du bruker funksjonskallet read_file("foo.txt")
og ønsker å åpne filen foo.txt, som ligger i samme mappe som python-filen du kjører, la oss si mappen labX. La oss videre tenke oss at labX i sin tur ligger i mappen inf100, og det er den sistnevnte mappen du har åpnet med VSCode. Da vil programmet krasje med en FileNotFoundError.
For å klare å åpne filen foo.txt ved å kjøre python fra VSCode, kan du gjøre ett av fire tiltak:
- flytte foo.txt til den mappen du har åpnet VSCode i (altså inf100 -mappen i vårt eksempel), eller
- åpne VSCode i den mappen foo.txt ligger i (altså labX -mappen i vårt eksemepel), eller
- endre funksjonskallet til
read_file("labX/foo.txt")
, eller - naviger først terminalen til mappen labX med
cd labX
-kommandoen, og start så programmet derfra.
Data processing
Når en fil har blitt åpnet i Python, har vi tilgang til innholdet, og kan bruke alt av python-funksjonaliteten for å jobbe med det. Vi kan for eksempel behandle innholdet ved å sortere, filtrere eller søke gjennom dataene.
Her får vi en tekstfil med makstemperaturer i løpet av en uke: temperatures.txt. Vi vil finne ut den høyeste og laveste temperaturen i løpet av uken. For å gjøre det leser vi først linjene fra filen og lagrer dem i en liste med tupler. Vi putter temperaturen først i tuplene fordi vi kan da direkte bruke sort()
til å sortere. sort()
vil sortere med hensyn til tuplenes første elementer i en liste med tupler. Dette er input-delen av programmet vårt der vi henter inn informasjon fra filen og velger en nyttig datastruktur.
Når vi har en liste med tupler sorterer vi den for å enkelt finne høyeste og laveste temperaturen. Dette er dataanalysedelen av programmet vårt, der vi jobber med dataene. Ved å holde denne delen avskilt, har vi mulighet å gjenbruke den med ulike datakilder.
Etter det presenterer vi resultatet ved å skrive ut på skjermen hvilken som var den kaldeste og den varmeste dagen i uken. Dette er presentasjonsdelen av programmet vårt, der vi presenterer resultatene. Det er igjen nyttig å holde denne separat fra analysedelen, det gir oss mulighet å skrive ut resultatene på andre måter enn bare å printe i terminalen.
# IO-part (placing the data into a data structure)
# =====================================================
with open("temperatures.txt", encoding="utf8") as f:
temp_day = []
for line in f:
day, temp = line.split()
temp_day.append((temp, day))
# =====================================================
# data processing/analysis
# =====================================================
# this will sort according to ascending temperature
temp_day.sort()
# print(temp_day)
# =====================================================
# IO-part (presenting the results)
# =====================================================
# day with the lowest temperature
print(
f"The coldest day was {temp_day[0][1]} with a temperature of {temp_day[0][0]} °C."
)
# day with the highest temperature
print(
f"The warmest day was {temp_day[-1][1]} with a temperature of {temp_day[-1][0]} °C."
)
# =====================================================
I dette eksempelet teller vi hvor mange ganger ulike bokstaver og ord blir brukt i hele boken ’Alice in wonderland’. Vi skal lese inn teksten fra en fil, istedenfor å lime inn hele teksten i python-filen, som vi gjorde før.
Til dette eksemplet må du laste ned denne tekstfilen: alice.txt.
# IO-part (placing the data into a data structure)
# =====================================================
with open("alice.txt", encoding="utf8") as f:
text = f.read()
# =====================================================
# data processing/analysis
# =====================================================
letter_count = {}
# opprett 0-verdi til alle bokstav som skal telles
for l in "abcdefghijklmnopqrstuvwxyz":
letter_count[l] = 0
# sjekk hvordan dict ser ut
# print(letter_count)
for let in text:
let = let.lower()
if let in letter_count:
letter_count[let] += 1
# =====================================================
# IO-part (presenting the results)
# =====================================================
for let, count in letter_count.items():
print(f"{let} is used {count:5d} times")
print("\n\n\n")
# =====================================================
# data processing/analysis
# =====================================================
word_count = {}
for word in text.split():
word = word.lower()
word_count.setdefault(word, 0)
word_count[word] += 1
def get_second(tpl):
return tpl[1]
sorted_result = sorted(word_count.items(), key=get_second, reverse=True)
# =====================================================
# IO-part (presenting the results)
# =====================================================
N = 15
print(f"The {N} most common words")
for w, c in sorted_result[:N]:
print(f"{w:14} is used {c:5d} times")
# =====================================================
Unntak / Try og except
Krasj av programmet kan være en bra ting, fordi vi ønsker å vite at noe er feil så fort som mulig. Men i mange tilfeller ønsker vi at programmet skal håndtere krasjen selv; dette gjelder f.eks når vi vet på forhånd hva slag krasj som kan oppstå når en bruker tar feil. Exceptions er ikke et lurt triks å bruke dette for å skyve problemer under teppet.
# Håndtere en krasj
# Prøv å gi programmet noe som ikke er et tall
# Prøv å gi programmet et all som er for stort
# Programmet krasjer ikke selv om brukeren gir ugyldige svar!
animals = ["katt", "hund", "kanin", "hamster", "krokodille"]
try:
i = int(input(f"Velg ett tall [1-{len(animals)}]: "))
animal = animals[i-1]
except:
# Kjøres dersom try-blokken krasjet
print("Ugyldig valg!")
else:
# Kjøres dersom try-blokken gikk bra
print("Gratulerer, du fikk en ny", animal)
print("Nå er programmet ferdig")
Bruk except
med varsomhet. Å bruke bare en except:
uten navnet til feilen kan gjøre koden din litt vanskeligere å feilsøke:
# FARE!! except håndterer for mange krasjer! Vanskelig å feilsøke!
# Prøv å gi programmet en gyldig tall som input
animals = ["katt", "hund", "kanin", "hamster", "krokodille"]
try:
i = int(input(f"Velg ett tall [1-{len(animals)}]:"))
animal = animal[i-1] # Skrivefeil! Vi VIL krasje her med NameError!
except:
print("Ugyldig valg!") # Oops! Kommer hit selv om input er gyldig
else:
print("Gratulerer, du fikk en ny", animal)
print("Nå er programmet ferdig")
Man bør alltid spesifisere hvilken type krasj man håndeterer.
# Håndere ulike typer feil
try:
i = int(input(f"Velg ett tall [1-{len(animals)}]:"))
animal = animals[i-1]
except ValueError:
print("Ugyldig valg, du må oppgi et tall!")
except IndexError:
print("Tallet du oppgav er ugyldig!")
else:
print("Gratulerer, du fikk en ny", animal)
print("Nå er programmet ferdig")
# NameError blir ikke fanget av except nå,
# så vi oppdager det nå hvis variabeler har feil navn.
Krasje på egen hånd
Kodeordet raise
brukes for å krasje på egen hånd.
food = input("Hva skal vi spise? ")
if food not in ["salat", "tomat", "agurk", "paprika"]:
raise ValueError(f"Maten '{food}' er ikke akseptabel.")
print("Takk for maten!")
Kan brukes for å gi ekstra informasjon ved krasj
def foo(i, j):
y = j
for x in range(i, j):
try:
y += abs(y/x)
except ZeroDivisionError as err: # err variabel som 'husker' krasjen
# Skriver ut debug-informasjon
print("En feil har oppstått:", err)
print("Lokale variabler: ", locals())
# Vi kan også kaste den samme krasj videre:
raise # prøv å fjerne denne og se forskjellen
return y
print(foo(-5, 10))