Uke 11 - modules / import / standardbibliotek¶
Eksempler¶
I Python kan vi dele opp kode i flere ulike filer. Dette blir raskt nødvendig når man gjør større prosjekt. Filene kalles moduler og man kan importere kode i en modul fra en annen modul. Dette skal vi se på denne uken.
Eksempel 1¶
Vi kan gjenbruke funksjoner og variabler fra en annen fil med import
. Her er
en fil med noe nyttig innhold (du kan laste ned filen her:
eks_1_lib.py
). Sånne filer kalles modul/bibliotek:
# Vi kan definere funksjoner...
def fib(n):
"""Return the fibonacci sequence up to n as a list"""
a, b = 0, 1
seq = []
while a < n:
seq.append(a)
a, b = b, a+b
return seq
def collatz(n):
"""Return the collatz sequence from n as a list"""
def collatz_step(n):
if n % 2 == 0:
return n//2
else:
return 3*n + 1
seq = [n]
while n > 1:
n = collatz_step(n)
seq.append(n)
return seq
# ...eller verdier i en bibliotek
# speed of light [m/s]
C_LIGHT = 299792458
# Planck constant [Js]
H_PLANCK = 6.62607015e-34
I hovedprogrammet kan vi gjenbruke filen på ulike måter: Vi kan import
-e hele
filen med sitt navn, eller vi kan gi et nytt navn med import .. as
. Se hvordan
vi bruker funksjoner fra filen.
En annen mulighet er å plukke ut spesifikke funksjoner med from .. import ...
.
Vi kan også bruke as
her. (du kan laste ned hovedprogrammet her:
eksempel_1.py
)
print('Option 1')
import eks_1_lib #import module
fib_1 = eks_1_lib.fib(100)
col_1 = eks_1_lib.collatz(17)
print(fib_1)
print(col_1)
#################################
print('Option 2')
import eks_1_lib as el
print(el.collatz(5))
c = el.C_LIGHT
print(c,'m/s')
#############################
print('Option 3')
from eks_1_lib import fib
fib_2 = fib(75)
print(fib_2)
##############################
print('Option 4')
from eks_1_lib import H_PLANCK as h, C_LIGHT as c, fib
print(c)
print(h)
print(fib(30))
Pass på at begge filene ligger i samme mappen, og at terminalen kjører derfra.
Ved bruk av import kan man strukturere store programmer på en oversiktlig måte, gjerne også i flere mapper. Det går utover pensum her, men mer informasjon finnes på https://docs.python.org/3/tutorial/modules.html
Noen ganger kan du se noe slikt som from module import *
i Python kode.
Dette importerer alle tingene fra modulen module
med sine egne navn. Dette
er ikke en rekommendert måte å importere. Om man importerer fra flere ulike
moduler/bibliotek er det vanskelig å si hvor det man bruker i koden er definert
om man vil se på den koden. Bruker man istedet from module1 import function1
og from module2 import function2
så ser man direkte at function1
er
definert i module1
og function2
er definert i module2
. Dessuten kan
det hende at et navn på en funksjon i modulen du importerer (som du ikke ens
bruker) klasjer med et navn i din kode.
Oppgave 1¶
Lag et bibliotek uke_11_oppg_1.py
i den samme mappen sånn at det følgende
programmet fungerer:
import uke_11_oppg_1
data = [3, 1, 7, -3, 5, 9, 1, 5, 9, 7, -3, 7]
a = uke_11_oppg_1.mean(data)
b = uke_11_oppg_1.median(data)
c = uke_11_oppg_1.mode(data)
print(a, b, c)
Mer spesifikasjon:
Du får ikke bruke de eksisterende funksjonene
mean
,median
ogmode
fra Python.Alle funksjonene skal ta én liste som argument.
Funksjonen
mean(xs)
skal returnere gjennomsnittet av tallene i listen. Gjennomsnittet av tallene \(x_1, x_2, ..., x_n\) beregnes med formelen:\[\frac{x_1 + x_2 + ... + x_n}{n}.\]For eksempel:
uke_11_oppg_1.mean([2, 5, 3, 1]) # skal returnere 2.75
Funksjonen
median(xs)
skal returnere medianen av tallene i listen. Dette er tallet som er i mitten etter att listen er blitt sortert. Om lengden av listen er jevn er medianen gjennomsnittet av de to tallene i mitten. For eksempel:uke_11_oppg_1.median([4, 12, 3, 9, 5]) # skal returnere 5 uke_11_oppg_1.median([3, 6, 93, 45, 14, 22]) # skal returnere 18
Funksjonen
mode(xs)
skal returnere typetallet: det vanligste tallet i listen. Om flere tall er like vanlige skal funksjonen returnere det tallet som står først i listen. For eksempel:uke_11_oppg_1.mode([3, 4, 22, 7, 4, 15, 4, 7, 1]) # skal returnere 4 uke_11_oppg_1.mode([3, 22, 7, 4, 15, 4, 7, 1, 4, 7, 22, 15, 22]) # skal returnere 22
Eksempel 2¶
Når du skriver python kode i en fil, finnes det alltid en (dold) variabel som
heter __name__
(her er det to understrekk «_» på hver side).
Verdien til __name__
er "__main__"
hvis filen kjøres direkte fra
terminalen. F.eks hvis man kjører eksempel_2.py
fra terminalen slik:
python eksempel_2.py
, så vil variabelen __name__
ha verdien
"__main__"
.
Hvis eksempel_2.py
importeres fra en annen python fil, det vil si den blir
ikke direkte kjørt fra terminalen, så er variabelen __name__
satt til
"eksempel_2"
, altså navnet på modulen.
Noen ganger vil vi kjøre noe kode i en fil KUN hvis filen blir kjørt direkte,
men ikke hvis den blir importert av en annen modul / python fil. For å gjøre
dette skriver vi if name == "__main__":
, og putter koden som skal kjøre hvis
vi kjører filen direkte fra terminalen innenfor denne if-clause.
Last ned eksempel_2.py og kjør filen fra terminalen. Skriver funksjonen
hello
ut? Hva er verdien på __name__
?
# if __name__ == "__main__" example
print('This is the top level, or 0 indentation level, of eksempel_2.py')
def hello():
print('hey there')
if __name__ == "__main__":
print('eksempel_2.py is being run directly')
print(f'{__name__} is the value of __name__') #what prints here?
else:
print('eksempel_2.py is being imported')
print(f'{__name__} is the value of __name__') # what prints here?
hello() # we only call function if file is imported
Nå skal vi se hva __name__
er hvis vi kjører python
rett fra terminalen.
Åpne en terminal i samme mappe som filen eksempel_2.py
, og skriv python
.
Skriv så import eksempel_2
. Hva skjer nå? Hva er verdien på __name__
?
Skriver funksjonen hello
ut denne gangen?
Standardbiblioteker¶
Mange nyttige biblioteker er allerede med i hver Python-installasjon, det er «Python Standard Library». Vi skal vise noen eksempler av nyttige moduler her.
En full oversikt finnes på Pythons hovedsiden:
https://docs.python.org/3/library/ Når man kommer til å jobbe mer med Python er
det lurt å bli kjent med oversikten her. Man trenger ikke å være ekspert i alle
modulene, men man må ha en idé hva slags moduler det finnes. Moduler som sys
eller os
er brukt i nesten alle prosjektene, mens biblioteker som f.eks
sunau
(Read and write Sun AU audio files) har en veldig spesialisert
målgruppe.
Dokumentasjonen for hvert bibliotek er veldig bra, med mange eksempler. Det finnes alltid noe nyttig når man leser gjennom. Du trenger ikke pugge modulene og bibliotekene vi skal diskutere denne uken. Bruk Python dokumentasjonen som en referanse.
Obs
Det kan ta litt tid til å bli vant med å lese dokumentasjon, men det er veldig bra å kunne. Et tips når du vil lese dokumentasjon er å først se på eksemplene og prøve å finne et eksempel som tilsvarer det du vil gjøre. Etter det leser du dokumentasjonen for de tingene som var med i eksemplet/eksemplene.
Eksempel 3¶
Pythons bibliotek math
inneholder mange vanlige matematiske funksjoner
og konstanter. Du finner dokumentasjonen for biblioteket på
https://docs.python.org/3/library/math.html.
Her er noen eksempler på bruk av math
(du kan laste ned koden her:
eksempel_3.py
):
import math
# The floor and ceiling functions
print(f"{4 / 3}")
print(f"{math.floor(4 / 3)}")
print(f"{math.ceil(4 / 3)}")
print()
# The constant e and powers of it
print(f"{math.e}")
print(f"{math.exp(1)}")
print(f"{math.exp(0)}")
print(f"{math.exp(2)}")
print()
# Logarithms
print(f"{math.log(math.e)}")
print(f"{math.log(1)}")
print(f"{math.log(5)}")
print()
print(f"{math.log2(2)}")
print(f"{math.log2(1)}")
print(f"{math.log2(5)}")
print(f"{math.log2(8)}")
print()
print(f"{math.log10(10)}")
print(f"{math.log10(1)}")
print(f"{math.log10(5)}")
print(f"{math.log10(100)}")
print()
# Exponentials
print(f"{math.pow(2, 3)}")
print(f"{math.pow(2, math.log2(3.3))}")
print(f"{math.pow(4, 1 / 2)}")
print(f"{math.sqrt(4)}")
print(f"{math.sqrt(math.pow(3, 4))}")
# print(f"{math.sqrt(-1)}") # This raises an error
# Use the library 'cmath' for complex numbers
print()
from math import pi
print(f"{pi}")
# The trigonometric functions take input in radians, not degrees
# What should all these values be?
print(f"{math.sin(pi / 2)}")
print(f"{math.sin(pi)}")
print(f"{math.cos(pi / 2)}")
print(f"{math.cos(pi)}")
print(f"{math.tan(pi)}")
print(f"{math.tan(pi / 2)}")
Det finnes også andre bibliotek for å gjøre beregninger. Her er en liste på numeriske biblioteker. Se på noen av dem og lag dine egne eksempler.
Eksempel 4¶
Om man trenger en kalender eller må beregne tidspunkt, er det best å bruke en ferdig bibliotek. I Python er det datetime.
Les gjennom siden og se på eksemplene der.
Filen eksempel_4.py
viser noen enkle bruk av biblioteken:
from datetime import date
today = date.today()
my_birthday = date(today.year, 6, 24)
if my_birthday < today:
my_birthday = my_birthday.replace(year=today.year + 1)
time_to_birthday = abs(my_birthday - today)
print(time_to_birthday)
from datetime import datetime
now = datetime.now()
exam = datetime(2021, 12, 6, 9, 0, 0)
print(exam - now)
Prøv å legge inn noe du fant i dokumentasjonen.
Oppgave 2¶
Følgende tekst er fra Project Euler nr.19:
«How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?»
Vi skal svare på dette spørsmålet for onsdager. I filen
uke_11_oppg_2.py
skriv kode som bruker datetime
-biblioteket til å
beregne og så skrive ut på skjermen hvor mange onsdager som føll på den 1.
i maneden fra 1. januar 1901 til 31 desember 2000.
Eksempel 5¶
Når vi vil telle antall kan vi bruke klassen Counter
i biblioteket collections
,
som er en spesiell versjon av dict(), hvor hvert element begynner med 0 allerede.
Dokumentasjonen finner du her:
collections.
Vi prøver igjen
å telle antall ord i en tekst, men denne gangen bruker vi Counter
. Du kan laste
ned koden her: eksempel_5.py
.
from collections import Counter
text = """Alice was beginning to get very tired of sitting by her sister
on the bank, and of having nothing to do: once or twice she had peeped
into the book her sister was reading, but it had no pictures
or conversations in it, 'and what is the use
of a book,' thought Alice 'without pictures or conversation?'"""
letter_count = Counter() # instead of dict() or {}
# Count letters
for let in text:
let = let.lower()
if let in "abcdefghijklmnopqrstuvwxyz":
letter_count[let] += 1
# With Counter we don't have to first create a key for every letter
for let, count in letter_count.items():
print(f"{let} is used {count:3d} times")
print("\n\n\n")
word_count = Counter()
for word in text.split():
word = word.lower()
word_count[word] += 1
# With Counter we don't have to first create a key for every word
# The 5 most used words, Counter has a useful helper function most_common()
# we don't need to sort ourselves
print("The 5 most common words")
print(word_count.most_common(10))
for w, c in word_count.most_common(5):
print(f"{w:14} is used {c:3d} times")
Eksempel 6¶
itertools-biblioteken
gjør det enklere å skrive ulike typer løkke. Filen eksempel_6.py
viser
et tilfelle, men det finnes mange eksempler i dokumentasjonen. Gjerne prøv noen
av dem.
import itertools
print('All DNA triples')
bases = 'ATGC'
# for i in bases:
# for j in bases:
# for k in bases:
# print(i+j+k)
for i,j,k in itertools.product(bases, repeat=3):
print(i+j+k)
Oppgave 3¶
I et spill har vi en kortstokk av 10 kort med verdiene
[2,3,4,5,6,7,8,9,10,11]
og målet er å samle \(k\) kort med total verdi
\(n\).
I filen uke_11_oppg_3.py
, skriv en funksjon som heter card_combinations(k,
n)
og som returnerer en liste med sorterte tupler på alle mulige kombinasjoner
av \(k\) kort med total verdi \(n\).
Bruk funksjoner fra itertools
For eksempel, card_combinations(3, 21)
skal returnere en slik liste:
[
(2, 8, 11),
(2, 9, 10),
(3, 7, 11),
(3, 8, 10),
(4, 6, 11),
...
Eksempel 7¶
Du kan bruke biblioteket random
til å generere tilfeldige tall.
Dokumentasjonen for dette biblioteket finner du her.
Du kan laste ned koden her: eksempel_7a.py
. Prøv å kjør koden noen
ganger uten random.seed(123457)
og noen ganger med random.seed(123457)
.
Hva er forskjellen?
import random
# random.seed(123456) # reproduserbare resultater, ulik for hver int
# randint() simulates a random integer
print(f"A random dice throw: {random.randint(1,6)}") # 1 <= N <= 6
print()
foods = ["pancakes", "soup", "stir fry", "lasagne"]
# choice() simulates a random choice among a collection of things
print(f"Today's lunch is: {random.choice(foods)}")
# choices() simulates a list of choices
print(f"This week's lunches are: {random.choices(foods, k=7)}")
print()
people = ["Person A", "Person B", "Person C", "Person D", "Person E"]
# sample() simulates a random sample among a collection of things
print(f"The people selected for the trial are: {random.sample(people, k=3)}")
print()
cards = []
for i in range(1, 14):
cards += [("hearts", i), ("clubs", i), ("spades", i), ("diamonds", i)]
print(f"Unshuffled card deck: {cards[:6]}...")
print()
# shuffle() simulates a random permutation
random.shuffle(cards)
print(f"Shuffled card deck: {cards[:6]}...")
print()
print(f"Player 1 takes the first 3 cards: {cards[:3]}")
print()
# random() without arguments simulates a uniform distribution
# over the interval [0.0, 1.0)
print(f"A random number in the interval [0.0, 1.0): {random.random()}")
print()
# uniform() simulates a uniform distribution over the given interval
print(
f"A random number in the interval [12, 20]: {random.uniform(12.0, 20.0)}"
) # 12.0 <= N <= 20.0
# gauss() simulates a normal distribution with the given
# the mean and standard deviation
mu = 40
sigma = 12
print(
f"A random number from a normal distribution with mean={mu}"
+ f" and standard deviation={sigma}: {random.gauss(mu, sigma)}"
)
Vi kan bruke random
til å se om vår egen implementasjon av en funksjon som
beregner standardavvikelse fungerer som den skall.
Du kan laste ned koden her: eksempel_7b.py
. Prøv å kjør koden noen
ganger med ulike verdier. Ser standard_deviation()
ut til å fungere som den
skal?
from random import gauss
from math import sqrt
from statistics import mean
# we define our own standard deviation function
def standard_deviation(data):
data_mean = mean(data)
s = sum([(x - data_mean) ** 2 for x in data])
return sqrt(s / (len(data) - 1))
if __name__ == "__main__":
# we check that it behaves as it should
sigma = 12 # try with different values
data = [gauss(40, sigma) for _ in range(20000)]
print(f"{standard_deviation(data) = }")
print(f"Expected value: {sigma}")
Oppgave 4¶
I filen uke_11_oppg_4.py
, gjør følgende:
Skriv en funksion
kast_n_2(n)
som tar inn et heltalln
og simulerer n kast av to terninger (random.randint(1,6)
) og legger til summen av de to i en liste. Returner den listen (som nå inneholder n elementer med tall mellom 2 og 12).Hvor ofte får vi øyesummen 2, 3, …, 12? Bruk
collections.Counter
i en funksjonprint_histo(xs)
som tar inn en slik liste og printer ut et enkelt histogram hvor'*'
står for 1%. Pass på formateringen av tallene og mellomrom
2 **
3 ***
4 *****
...
10 ***
11 **
12 *
(Ikke bruk random.seed() slik at testene kan fungere rett.)
Eksempel 8¶
Biblioteken fractions og decimal kan brukes til beregninger om man vil undvike avrundingsfeil som man får om man bruker float.
For å lage en desimal skriver du for eksempel Decimal("0.2")
. For å lage en
brøkdel skriver du for eksempel Fraction(10, 5)
. Det første argumentet er
teller og det andre argumentet er nevner. Fraction
kan gjøre forenklinger
akkurat som vi gjør på papir når vi regner med brøkdeler. Vi kan også bruke
Fraction
til å finne den nermeste ratsjonelle approsimasjonen av et
irratsjonellt tall med en øvre grens på nevneren.
from decimal import Decimal
from fractions import Fraction
from math import pi
# using Decimal avoids rounding errors
print(f"Using floats: {0.1 + 0.2 = }")
print(f"Using Decimal: 0.1 + 0.2 = {Decimal('0.1') + Decimal('0.2')}")
print()
print(f"Using floats: {(1/14) * (7/3) = }")
print(f"Using Fraction: (1/14) * (7/3) = {Fraction(1, 14) * Fraction(7, 3)}")
print()
# we can use the fractions library to find the closest fraction to a given number
print(
"The closest fraction to π with denominator at most 1000 is:",
str(Fraction(pi).limit_denominator(1000)),
)
Oppgave 5¶
I denne oppgaven skal du bruke biblioteket decimals til å skrive et program som produserer en kvittering fra en butikk.
Last ned uke_11_oppg_5.py
og spar den på datamaskinen din (med samme
navn). Filen ser slik ut:
def receipt_content(prices_filename, cash_register_filename):
"""Construct contents of a receipt of the cash register events,
given the store prices."""
# din kode her
def receipt(prices_filename, cash_register_filename):
"""Construct a receipt of the cash register events,
given the store prices."""
# get receipt content from receipt_content()
purcases_returns, total, vat, payment, change = receipt_content(
prices_filename, cash_register_filename
)
# the formatted lines of the receipt
receipt_lines = [f"{'Nr.':>4} {'Item':18} {'Price':>10}"]
for nr, item, price in purcases_returns:
receipt_lines.append(f"{nr:4d} {item:18} {price:10.2f}")
receipt_lines.append(f"Total: {total}")
receipt_lines.append(f"Of which VAT: {vat:.2f}")
receipt_lines.append(f"Payment: {payment}")
receipt_lines.append(f"Change {change}")
# add some dividers
max_len = max(len(line) for line in receipt_lines)
divider = "-" * max_len
receipt_lines.insert(1, divider)
receipt_lines.insert(-4, divider)
receipt_lines.insert(-2, divider)
return "\n".join(receipt_lines)
Du skal skrive koden til funksjonen receipt_content()
som beregner
inneholdet i kvitteringen utifra en fil med butikkens priser og en fil med
hendelsene ved kassen. Argumentet prices_filename
er en streng med navnet
til filen som inneholder prisene. Argumentet cash_register_filename
er en
streng med navnet til filen som inneholder hendelsene ved kassen.
Funksjonen receipt_content()
skal returnere en tupel som inneholder følgende
(i denne orden):
En liste med tupler som inneholder (i følgende orden) antall, produkt og totalt pris, først for alle ting som har blitt kjøpt, i alfabetisk orden, og så for alle ting som har blitt returnert, i alfabetisk orden (se eksempelkvittering nedenfor). For de returnerte produktene blir det negative tall.
Det totale priset.
Hvor mye av det totale priset som er mva.
Hvor mye som har blitt betalt.
Hvor mye som blir betalt tilbake (her blir det ikke-positive tall).
Filen med butikkens priser er formattert slik som eksempelfilen
prices.txt
(som du kan bruke til å teste programmet ditt):
banana;7.40
dish soap;26.20
toilet paper;34.00
apple;5.00
frozen pizza;54.40
stamp;3.00
bread;22.20
ice cream;35.10
toothpaste;13.70
pocket book;149.00
chips;24.30
tomato;10.00
peanuts;18.50
Først står produkt, så «;», og så priset.
Filen med hendelsene ved kassen er formattert slik som eksempelfilen
cash_register.txt
(som du kan bruke til å teste programmet ditt):
buy;apple
buy;dish soap
buy;apple
buy;toilet paper
buy;frozen pizza
buy;peanuts
return;toothpaste
buy;tomato
buy;chips
buy;tomato
return;pocket book
buy;tomato
pay;100.00
Først står hva som skjer (kjøp, retur eller betaling), så «;», og så produkten/verdien.
Funksjonen receipt()
skal du ikke endre. Den bruker receipt_content()
til å beregne inneholdet i kvitteringen og så produserer receipt()
en pen
kvittering utifra det innholdet. Du skal bare skrive kode til
receipt_content()
. Om din kode til receipt_content()
er riktig så skal
du for eksempel få følgende streng når du bruker receipt("prices.txt",
"cash_register.txt")
:
Nr. Item Price
------------------------------------
2 apple 10.00
1 chips 24.30
1 dish soap 26.20
1 frozen pizza 54.40
1 peanuts 18.50
1 toilet paper 34.00
3 tomato 30.00
-1 pocket book -149.00
-1 toothpaste -13.70
------------------------------------
Total: 34.70
Of which VAT: 6.94
------------------------------------
Payment: 100.00
Change -65.30
Din kode skal inneholde to egendefinerte exceptions:
Om priset til en produkt som kjøpes/returneres ikke finnes i prislisten skal koden produsere (
raise
) en egendefinert Exception som heterNoPrice
(uten melding). (Denne skal ikke fanges av programmet uten programmet skal krasje med denne feiltypen.)Om kunden har ikke betalt nok skal koden produsere (
raise
) en egendefinert Exception som heterNotEnoughPaid
(uten melding). (Denne skal ikke fanges av programmet uten programmet skal krasje med denne feiltypen.)
Mer spesifikasjon og tips:
Du må bruke biblioteket decimals for at beregningene skal bli riktige (prøv hvordan kvitteringen blir om du bruker
float
).Det er mulig at betaling skjer flere ganger underveis ved kassen.
Du kan anta at alle betalinger ved kassen har positiv verdi.
Du kan anta at det ikke er noen feil i filene som inneholder prisene og hendelsene ved kassen.
Det er mulig at det totale priset blir negativt (om kunden for eksempel bare returnerer ting).
Gitt et pris inklusive mva så multipliserer du priset med \(0.2\) for å finne ut hvor mye av priset som er mva.
Det kan være lurt å bruke dictionaries til prislisten og det som blitt kjøpt og det som blitt returnert.
Du må ikke skrive all kode innen funksjonen
receipt_content()
. Du kan dele opp koden i flere funksjoner på en måte du synes føles naturlig.
Oppgaver¶
Oppgave 1¶
Du finner uke_11_oppgave_1 nedenfor Eksempel 1.
Oppgave 2¶
Du finner Oppgave 2 nedenfor Eksempel 4.
Oppgave 3¶
Du finner Oppgave 3 nedenfor Eksempel 6.
Oppgave 4¶
Du finner Oppgave 4 nedenfor Eksempel 7.
Oppgave 5¶
Du finner Oppgave 5 nedenfor Eksempel 8.