Uke 10 - Filer og Exceptions¶
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
Eksempler - Filer¶
Eksempel 1¶
I dette eksemplet skal vi prøve å lese og skrive til en fil. Du må laste ned
tekstfilen: timemachine.txt
og Python-filen:
eksempel_1.py
og ha dem i samme mappe på datamaskinen din. Åpne så mappen i VSCode.
Obs
Åpne timemachine.txt
i VSCode for å se innholdet og formateringen.
Kjør Python-filen. Det valgfrie argumentet encoding
til funksjonen
open()
sier med hvilken encoding filen skal leses. Siden Timemachine-filen er kodert med UTF-8, nevner vi det her.
Hva er forskjellen mellom for line in f
, f.readlines()
og f.read()
for å lese fra filen?
Hva skjer når du åpner filen ’numbers.txt’ med mode ’w’ i slutten?
# 3 ways of getting information from a file
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:], "<<<")
# ========
with open("timemachine.txt", encoding="utf8") as f:
all_lines = f.readlines()
print(len(all_lines), "lines in the text")
# ==========
with open("timemachine.txt", encoding="utf8") as f:
all_lines = f.read()
print("vvvvvvv")
print(all_lines)
print("^^^^^^^")
# ========
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:
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!
Oppgave 1¶
I filen uke_10_oppg_1.py
, skriv en funksjon som heter
open_file(filename)
som åpner en fil med navn filename
og returnerer én
streng som inneholder hele filinnholdet. (Denne strengen skal altså inneholde
alle newlines og whitespaces som funnets i tekstfilen.)
Obs
Du kan bruke askeladden.txt
som en test fil, men funksjonen skal
fungere med forskjellige filnavn.
Oppgave 2¶
I filen uke_10_oppg_2.py
, skriv en funksjon som heter
open_file(filename)
som åpner en fil med navn filename
og lagrer
innholdet til filen som én streng med >>>
foran hver linje og <<<
etter.
Returner denne strengen. (Hvor det er radbrytninger i tekstfilen skal det og være
det i strengen.)
Eksempelkjøring med askeladden.txt
:
>>>Det var en gang en konge, og den kongen hadde hørt snakk om et skip som gikk like fort til lands som til vanns.<<<
>>>Så ville han ...
...
Eksempel 2¶
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 temperaturene fra
filen og lagrer i en liste med tupler. Vi putter temperaturen først i tuplene
fordi da kan vi direkte bruke sort()
til å sortere på temperaturen
(sort()
vil sortere med hensyn til tuplenes første elementer i en liste med
tupler). Dette er IO-delen av programmet vårt.
Når vi har en liste med tupler sorterer vi den for å enkelt finne høyeste og laveste temperaturen. Dette er dataanalysdelen av programmet vårt.
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.
Her er koden: eksempel_2.py
. Skjønner du hva som skjer?
# 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."
)
# =====================================================
Oppgave 3¶
I filen uke_10_oppg_3.py
, skriv en funksjon som heter r_w_file(infile,
outfile)
som åpner filen med navn infile
(som er en fil med temperaturer i
løpet av en uke, slik som temperatures.txt
), looper over linjene på
filen og lager en ny fil med kun de linjene hvor temperaturen er 23.5 °C eller
mer. Denne nye filen skal ha navnet fra outfile
variablen. (Om ingen dag har
en temperatur som er minst 23.5 °C så skal outfile
være tom.)
Eksempelkjøring med temperatures.txt
:
Monday 23.5
Wednesday 24.0
Thursday 23.9
Sunday 23.9
Eksempel 3¶
I dette eksempelet teller vi hvor mange ganger ulike bokstaver og ord blir brukt i hele boken ’Alice in wonderland’. Derfor vil vi lese in teksten fra en fil, istedet for å lime hele teksten inn i filen med python-kode, som vi gjorde før.
Til dette eksemplet må du laste ned denne tekstfilen: alice.txt
.
Koden er i denne filen: eksempel_3.py
. Kjør koden. Skjønner du
hva som skjer?
# IO-part (placing the data into a data structure)
# =====================================================
with open("alice.txt", encoding="utf8") as f:
text = f.read()
# =====================================================
### resten er lik programmet fra før
# data processing/analysis
# =====================================================
letter_count = {}
for l in "abcdefghijklmnopqrstuvwxyz":
letter_count[l] = 0
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")
# =====================================================
Oppgave 4¶
I filen uke_10_oppg_4.py
, skriv en funksjon som heter
first_letter_last_word(filename)
som åpner en fil med navn filename
, looper over
linjene i filen, og tar ut den første bokstaven til det siste ordet i hver
linje. Returner disse bokstavene som én streng.
Eksempelkjøring med askeladden.txt
:
'vlf'
Eksempler - Exception handling¶
Du har støtt på exceptions før i denne kursen, men i alle de tilfellene så krasjet programmet. Nå skal vi se på hvordan man kan håndtere exceptions sånn at programmet ikke krasjer.
Eksempel 4¶
En vanlig type feil når man bruker lister er IndexError. Det får man når man prøver å hente et element fra et index som er utenfor listen.
Her er et eksempel på dette. Prøv å kjøre denne koden:
xs = ['a', 'b', 'c']
a = xs[5] # fails with IndexError
print(a)
Vi kan håndtere feilet ved hjelp av try-except
. Dette kan vi gjøre på noen
forskjellige måter. Vi kan enten fange alle feil med å bruke except Exception
eller så kan vi bare fange IndexErrors ved å bruke except IndexError
. Vi kan
også fange både IndexErrors og TypeErrors ved å bruke except IndexError
og
except TypeError
etter hverandre.
Her er eksempel på dette. Last ned koden her: eksempel_4.py
og kjør den. Skjønner du forskjellen mellom de ulike måtene å bruke
except
?
xs = ["a", "b", "c"]
# print(xs[5]) # fails with IndexError
# Option 1 - catch everything
try:
x1 = xs[2]
x2 = xs[5]
print(x2 - x1)
except Exception:
print("Something went wrong")
print("Decide what to do next here")
# ...
# Option 2 - catch specific
try:
x1 = xs[2]
x2 = xs[5] # broken
print(x2 - x1)
except IndexError as err:
print("We caught an IndexError:", err)
# Option 3 - TypeError still goes out
try:
x1 = xs[2]
x2 = xs[0]
print(x2 - x1) # broken
except IndexError as err:
print("We caught an IndexError:", err)
# get TypeError in Terminal
# Option 4 - both caught
try:
x1 = xs[2]
x2 = xs[0]
print(x2 - x1) # broken
except IndexError as err:
print("We caught an IndexError:", err)
except TypeError as err:
print("We caught a TypeError:", err)
Oppgave 5¶
I filen uke_10_oppg_5.py
, skriv en funksjon som heter
first_letters(filename)
som bruker first_letter_last_word(filename)
fra oppgave 4. Funksjonen
skal ta et filnavn som argument og returnere en streng med den første bokstaven
i det siste ordet for hver linja av filen. Hvis filen ikke finnes skal den
returnere ""
.
Tips: Hvis en funksjon forsøker å åpne en fil som ikke finnes vil Python signalisere (raise) en FileNotFoundError.
Eksempel 5¶
I denne oppgaven skal brukeren gi heltall (så mange som brukeren vil) og så skal
programmet skrive ut summen av alle tallene. Her må vi bruke try-except
for
at ikke programmet skal krasje om brukeren angir noe som ikke er et heltall. Det
som kan lage en exception i dette tilfellet er om vi forsøker å konvertere en
streng som ikke tilsvarer et heltall, til et heltall. Da får vi ValueError
.
Derfor må vi fånge det med hjelp av except ValueError
.
Last ned koden her eksempel_5.py
og kjør den. Skjønner du hva som
skjer?
number_sum = 0
while True:
input_str = input("Give me an integer (q to quit): ")
if input_str == "q":
break
try:
number = int(input_str)
except ValueError:
print("That was not an integer. Please try again.")
continue
number_sum += number
print(f"The sum is {number_sum}.")
Oppgave 6¶
Dette er en enkel funksjon, men den kan kræsje programmet hvis vi blander datatyper:
def add_together(a, b, c, d):
return a + b + c + d
I filen uke_10_oppg_6.py
, skriv en ny funksjon add_together_safely(a, b,
c, d)
som tar 4 argumenter og bruker denne funksjonen på dem. Hvis ingenting
går galt skal du returnere resultatet av add_together(x, y, z, w)
. Hvis noe går galt
skal du skrive ut Failed with error:
og så feilmeldingen, til slutt skal
funksjonen returnere None
. Bruk try-except
format i funksjonen.
Eksempelkjøring (i interaktiv modus):
>>> add_together_safely(1, 2, 3, 4)
10
>>> add_together_safely('a', 'b', 'c', 'd')
'abcd'
>>> add_together_safely(1, 2, 'c', 'd')
Failed with error: unsupported operand type(s) for +: 'int' and 'str'
Oppgave 7¶
I filen uke_10_oppg_7.py
, skriv din egen versjon av dictionary-funksjonen
get
. Funksjonen skal hete my_get(d, k, v)
og ta tre argumenter: en
dictionary, en nøkkel og en default-verdi. Hvis nøkkelen finnes i dictionary
skal den returnere verdien til nøkkelen, og ellers skal den returnere
default-verdien.
Bruk try/except
i løsningen din.
Eksempel 6¶
I dette eksemplet skal vi lese inn tall fra en fil. Om det er noen feil i filen som blir leset inn kommer vi få en error i koden. Dette håndterer vi med try-except.
Last ned koden her: eksempel_6.py
. Her fanger vi FileNotFoundError
og ValueError for seg selv og så alle andre feil. Kan det bli noen
andre feil enn FileNotFoundError og ValueError når vi bruker funksjonen
get_numbers_from_file()
?
def get_numbers_from_file(filename):
"""
Read numbers from a file.
The file format is a simple column like:
31
145
-947
If anything goes wrong, return an empty list
"""
try:
data = []
with open(filename) as f:
for line in f:
number = int(line)
data.append(number)
return data
except FileNotFoundError:
print(f"Warning: {filename} does not exist.")
return []
except ValueError:
print(f"Warning: {filename} contains items that are not integers.")
return []
except Exception:
print("Warning: Unknown problem")
return []
# if __name__ == "__main__":
# make your own files and your own function calls to get_numbers_from_file() here
Her putter vi try
rundt flere rader kode. Dette betyr at vi har litt mindre
å jobbe med om vi får en exception, siden vi da ikke kan bruke noe av koden som
er under try
. I dette eksemplet betyr det at vi ikke kan returnere tallene
som var riktige i filen, uten vi må returnere en tom liste.
Prøv nå å lage dine egne filer og bruk funksjonen get_numbers_from_file()
i hovedprogrammet. Et kall av funksjonen skal være uten
feil, et skal gi FileNotFoundError og et skal gi ValueError.
Eksempel 7¶
Det er ofte mulig å plassere try-except på ulike steder i koden sin. Da må man velge det stedet hvor det er mest naturlig at et feil blir håndtert.
Last ned filen her: eksempel_7_1.py
. Funksjonen
all_lines_through_points()
skal printe ligningen for alle linjer
mellom alle fire punkter den får som input (som en liste). Om det ikke
er mulig å beregne ligningen mellom et par av punkter skal den printe
hvorfor dette ikke er mulig og siden gå videre til neste par (programmet
skal ikke krasje).
Om du prøver å kjøre koden så krasjer den. Plassere ut try-except hvor du synes det er mest naturlig, slik at koden fungerer som den skal. Hvorfor har du valgt å plassere try-except der du har plassert det?
def slope(x1, y1, x2, y2):
return (y2 - y1) / (x2 - x1)
def y_intercept(x1, y1, x2, y2):
return (x2 * y1 - x1 * y2) / (x2 - x1)
def line_eqn_from_points(p1, p2):
# p1, p2 are 2-tuples
x1, y1 = p1
x2, y2 = p2
a = slope(x1, y1, x2, y2)
b = y_intercept(x1, y1, x2, y2)
return f"y = {a}x + {b}"
def all_lines_through_points(points):
num_points = len(points)
for i in range(num_points):
for j in range(i + 1, num_points):
p1 = points[i]
p2 = points[j]
eq = line_eqn_from_points(p1, p2)
print(f"The equation for the line between {p1} and {p2} is {eq}.")
if __name__ == "__main__":
all_lines_through_points([
(1, 1),
(-1, 1),
(1, -1),
(-1, 1),
])
Last ned filen her: eksempel_7_2.py
som er samme kode som
eksempel_7_1.py
men med try-except, slik at koden ikke krasjer.
Her er try-except plassert i funksjonen all_lines_through_points()
.
Om try-except er i noen av funksjonene slope()
og y_intercept()
må vi si hva de funksjonene skal returnere om det ikke er mulig å beregne
linjens stigning eller konstantledd. Her finnes det ingen selvfølgelig svar.
Om funksjonene returnerer noen string med feilmelding må vi alltid sjekke
hva vi får når vi bruker funksjonene og se om vi får et tall eller en
feilmelding. Det er bedre om vi vet at vi alltid får et tall fra funksjonene,
om de ikke krasjer.
Det er samme med å plassere try-except i line_eqn_from_points()
. Da må
vi si hva den funksjonen skal returnere om det blir en error. Når vi så
bruker line_eqn_from_points()
må vi alltid sjekke om strengen vi får er
en feilmelding eller en ligning. Det er bedre å vite at all_lines_through_points()
alltid returnerer en ligning, om den ikke krasjer.
Men i funksjonen all_lines_through_4_points()
vet vi hva som skal skje
om det ikke går å beregne ligningen for linjen mellom to punkter. Da skal
vi printe en melding om dette og hvorfor det ikke er mulig. Derfor passer
det best å plassere try-except her.
def slope(x1, y1, x2, y2):
return (y2 - y1) / (x2 - x1)
def y_intercept(x1, y1, x2, y2):
return (x2 * y1 - x1 * y2) / (x2 - x1)
def line_eqn_from_points(p1, p2):
# p1, p2 are 2-tuples
x1, y1 = p1
x2, y2 = p2
a = slope(x1, y1, x2, y2)
b = y_intercept(x1, y1, x2, y2)
return f"y = {a}x + {b}"
def all_lines_through_points(points):
num_points = len(points)
for i in range(num_points):
for j in range(i + 1, num_points):
p1 = points[i]
p2 = points[j]
try:
eq = line_eqn_from_points(p1, p2)
print(f"The equation for the line between {p1} and {p2} is {eq}.")
except Exception:
if p1 == p2:
print(
f"Cannot compute line equation between the identical points {p1} and {p2}."
)
else:
print(
f"Cannot compute line equation for vertical line between points {p1} and {p2}."
)
continue
if __name__ == "__main__":
all_lines_through_points(
[
(1, 1),
(-1, 1),
(1, -1),
(-1, 1),
]
)
# find place where you can explain _why_ the exception happened, not just _that_ it happened
Eksempel 8¶
Vi kan produsere exceptions manuellt ved å bruke raise
. I eksemplet vil vi
definere en funksjon som konstruerer nullvektoren av en gitt dimensjon, men
dette er bare definert for ikke-negative heltall. Så om vi får et heltall som er
negativt bruker vi raise ValueError
til å manuellt produsere et
ValueError
. I dette tilfellet sender vi også med en melding om hva som er
feil: raise ValueError("negative dimension")
, men man må ikke sende med en
melding, da skriver man bare raise ValueError
.
Last ned koden her: eksempel_8_1.py
, og kjør den. Skjønner du hva
som skjer? Hvorfor krasjer programmet om vi kjør koden
print(zero_vector(-1)
?
def zero_vector(dimension):
if dimension < 0:
raise ValueError("negative dimension") # we can raise a built in exception
return [0] * dimension
print(zero_vector(4))
print(zero_vector(0))
# print(zero_vector(-1)) # causes the program to crash
Vi kan også definere egne feil. I dette eksemplet definerer vi feiltypen
NoData
. Programmet skal spørre brukeren om et dato eller en temperatur. Om
brukeren gir et dato skal programmet skrive ut temperaturen på det datoet. Om
brukeren velger en temperatur skal programmet skrive ut alle datoer med den
temperaturen. Men om det ikke finnes data som tilsvarer det brukeren spør om
produserer vi et feil av typen NoData
som så håndteres og så skriver
programmet ut at det ikke finnes tilsvarende data. I dette tilfellet sender vi
ikke med en melding når vi bruker raise
.
Last ned koden her: eksempel_8_2.py
, og kjør den. Skjønner du hva
som skjer? Hvordan kan vi håndtere når det ikke finnes tilsvarende
temperaturdata uten å bruke exceptions? Synes du det er bedre å håndtere
utilgjengelig temperaturdata med eller uten å bruke exceptions? Hvorfor?
# we can define our own exceptions
class NoData(Exception):
pass
temperature_data = [
("2020-03-22", 12.3),
("2020-03-23", 13.2),
("2020-03-24", 11.3),
("2020-03-25", 15.0),
("2020-03-26", 16.9),
("2020-03-27", 10.4),
("2020-03-28", 17.3),
("2020-03-29", 15.2),
("2020-03-30", 16.9),
]
def get_temp(date):
dates, temps = zip(*temperature_data)
if date not in dates:
raise NoData # we raise our own exception
temp = None
for i, d in enumerate(dates):
if d == date:
temp = temps[i]
break
return temp
def get_dates(temp):
dates, temps = zip(*temperature_data)
if temp not in temps:
raise NoData # we raise our own exception
matching_dates = []
for i, t in enumerate(temps):
if t == temp:
matching_dates.append(dates[i])
return matching_dates
choice = input("Do you want to specify a date [d] or a temperature [t]? ")
try:
if choice == "d":
date = input("Date: ")
temp = get_temp(date)
print(f"The temperature on {date} was {temp} °C.")
elif choice == "t":
temp = input("Temperature: ")
dates = get_dates(float(temp))
print(f"The temperature was {temp} on the following days:")
for date in dates:
print(date)
except NoData: # we catch any NoData exception here
print("There is no data that matches your request.")
Oppgave 8¶
Prikkproduktet av to vektorer er resultatet av å gange sammen hvert element, og så legge sammen disse. Men prikkproduktet er bare definert hvis vektorene er like lange!
I filen uke_10_oppg_8.py
, skriv en funksjon som heter dot_product(a,b)
som tar to lister som argumenter og returnerer prikkproduktet av disse. Hvis det
ikke går ann å ta prikkproduktet (listene ikke er like lange) skal funksjonen
signalisere (raise) en ValueError.
Oppgaver¶
Obs
Du kan bruke askeladden.txt
som en test fil, men funksjonene skal
fungere med ulike filnavn).
pytest
-filene lastes ned uke_10_tests.zip
Oppgave 1¶
Du finner Oppgave 1 nedenfor Eksempel 1.
Oppgave 2¶
Du finner Oppgave 2 nedenfor Eksempel 1.
Oppgave 3¶
Du finner Oppgave 3 nedenfor Eksempel 2.
Oppgave 4¶
Du finner Oppgave 4 nedenfor Eksempel 3.
Oppgave 5¶
Du finner Oppgave 5 nedenfor Eksempel 4.
Oppgave 6¶
Du finner Oppgave 6 nedenfor Eksempel 5.
Oppgave 7¶
Du finner Oppgave 7 nedenfor Eksempel 5.
Oppgave 8¶
Du finner Oppgave 8 nedenfor Eksempel 8.
Oppgave 9 - Submarine Course¶
Tilpasset fra Advent of Code 2021, Day 2 <https://adventofcode.com/2021/day/2>:
Gratulerer! Du lærer å styre en ubåt.
Ubåten kan ta en rekke kommandoer som forward 1
, down 2
eller up 3
:
forward X
øker den horisontale posisjonen medX
enheter.down X
øker dybden medX
enheter.up X
reduserer dybden medX
enheter.
Legg merke til at siden du er på en ubåt, påvirker ned og opp dybden din, og dermed får de det motsatte resultatet av det du kan forvente.
Ubåten har allerede en planlagt kurs (din input). Du bør nok finne ut hvor det går. For eksempel:
forward 5
down 5
forward 8
up 3
down 8
forward 2
Din horisontale posisjon og dybde starter begge på 0
. Trinnene ovenfor vil da endre dem som følger:
forward 5
legger til5
til din horisontale posisjon, totalt5
.down 5
legger til5
til dybden din, noe som resulterer i en verdi på5
.forward 8
legger til8
til din horisontale posisjon, totalt13
.forward 2
legger til2
til din horisontale posisjon, totalt15
.
Etter å ha fulgt disse instruksjonene vil du ha en horisontal posisjon på 15
og en dybde på 10
. (Å multiplisere disse sammen gir 150
.)
I filen uke_10_oppg_9.py
, skriv to funksjonene:
sub_course_positions(path)
som tar som input en.txt
kurs-filen og returneres endict
som inneholder den horisontale posisjonen og dybden du ville ha etter å ha fulgt den planlagte kursen fra filensub-path-full.txt
. Brukforward
ogdepth
som nøklene tildict
ditt.
Eksempelkjøring med sub-path-sample.txt
filen:
>>> sub_course_positions(path)
{"forward": ... , "depth": ...}
sub_course(path)
som ta som input en.txt
kurs-filen, multipliserer den endelige horisontale posisjonen din med den endelige dybden, og returneres enint
. Gjerne bruke din sub_course_positions(path) funksjonen i denne funksjonen. Bruk den funksjonen å beregne resultat fra kurs-filensub-path-full.txt
.
Eksempelkjøring med sub-path-sample.txt
filen:
>>> sub_course(path)
150
Oppgave 10 - Turtle Commands¶
I filen uke_10_oppg_10.py
, skriv to funksjonene.
Den første skal heter write_turtle_str(file_in)
som leser inn en liste med instruksjoner fra en tekstfil og omskriver hver linje til en sekvens av skilpaddekommandoer som returneres i en string
. Den første linjen må være import turtle as t
og den siste linjen av instruktsjonene må være t.done()
Obs
Tekstfilen kan inneholde kommandoene forward
, right
, left
, up
, og down
som betyr det samme som de tilsvarende turtle-kommandoene. Kommandoene up
og down
har ingenting i neste posisjon i tekstfilen - det betyr at turtle-kommandoen har tomt parentes, f.eks, t.up()
Den andre funksjonen skal hete turtle_file_out(turtle_str, file_out)
, og den burde ta inn strengen som den første funksjonen opprettet og skrive den ut til en ny tekstfil.
Bruk turtle-path-1.txt
for å utvikle funksjonen din, men den skal fungere også med alle andre tekstfiler som har denne formateringen.
Eksempelkjøring:
import turtle as t
t.forward(50)
t.right(90)
...
t.down()
...
...
t.done()