Standardbiblioteket

Python har mange moduler som er innebygget i selve språket, men som likevel ikke er umiddelbart tilgjengelig uten av vi importer dem først. Slike moduler er en del av Python sitt standardbibliotek, og inkluderer moduler som math, random, copy, time, datetime, csv, decimal, sys, os og mange andre. Se docs.python.org/3/library for en fullstendig oversikt.

For å bruke en modul fra python sitt standardbibliotek, holder det å skrive import <modulnavn>. Konvensjon tilsier at dette gjøres øverst i filen.

Under viser vi frem et par eksempler fra noen utvalgte moduler fra Python sitt standardbibliotek.

Matematikk

Dato og tid

Håndtere vanlige filformater

Interaksjon med operativsystemet og filstrukturen


math

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

import math

# Noen utvalgte konstanter
print(math.pi)                 # 3.141592653589793
print(math.e)                  # 2.718281828459045
print(math.inf)                # uendelig
print(-math.inf)               # minus uendelig
print()

# Noen utvalgte funksjoner
print(math.ceil(3.22))         # 4, runder alltid av oppover
print(math.floor(3.9))         # 3, runder alltid av nedover
print(math.radians(180))       # 3.14..., konverter grader til radianer
print(math.degrees(math.pi/2)) # 90.0, konvertere radianer til grader
print(math.cos(math.pi))       # -1.0, cosinus-funksjonen
print(math.factorial(4))       # 24, faktorial-funksjonen (24 = 1*2*3*4)
random

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

import random

# Et tilfeldig flyttall mellom 0 og 1
x = random.random()
print(x)

# Et tilfeldig element fra en liste/samling
a = ['foo', 'bar', 'baz']
s = random.choice(a)
print(s)

# Et tilfeldig tall mellom 0 og 9
y = random.randrange(10) # ca det samme som random.choice(range(10))
print(y)

Ved å sette et «frø» kan vi få samme sekvens av «tilfeldige» tall hver gang vi kjører programmet. Dette kan være nyttig for testing og debugging, eller hvis du ønsker å kunne gjenskape et eksperiment. Som standard er frøet satt til systemtiden: antall nanosekunder siden 1. januar 1970 delt på 100.

import random
random.seed(42) # Vi setter frøet til 42 (kan være hva som helst)

# Kjør programmet mange ganger og observer: de samme valgene gjentar
# seg hver gang man kjører programmet på nytt med samme frø.
a = ['foo', 'bar', 'quz']
print(random.choice(a))
print(random.choice(a))
print(random.choice(a))
print(random.choice(a))

Random-modulen har massevis av mer avanserte muligheter også, for eksempel å velge tilfeldige tall fra en rekke ulike fordelinger (uniform, normal, gammavariat, etc.). Det finnes også funksjoner for å velge flere tilfeldige elementer fra en samling med og uten tilbakelegging (henholdsvis random.choices og random.sample), samt for å blande en samling (med random.shuffle). Se offisiell dokumentasjon for mer informasjon.

time

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

import time

print(time.time()) # Antall sekunder siden 1. januar 1970 som flyttall
datetime

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

from datetime import datetime, timedelta

# Et datetime -objekt representerer et bestemt tidspunkt
lecture_starts = datetime(2024, 3, 22, 12, 15, 00)
print(f"{lecture_starts = }")
print(f"{lecture_starts.year = }, {lecture_starts.month = }")
print(f"Ukedag: {lecture_starts.weekday()} (0=Mandag, 6=Søndag)")
print()

# Et timedelta -objekt representerer en gitt varighet
lecture_duration = timedelta(hours=1, minutes=45)
print(f"{lecture_duration = }")

# Forholdstall mellom varigheter kan brukes for å telle hvor mange
# dager/timer/sekunder/millisekunder det er i en varighet.
minute = timedelta(minutes=1)
print(f"{lecture_duration / minute = }") # Antall minutter totalt
hour = timedelta(hours=1)
print(f"{lecture_duration / hour = }")  # Antall timer
print()

# Tidspunkt + varigheter gir et nytt tidspunkt
lecture_ends = lecture_starts + lecture_duration
print(f"{lecture_ends = }")
print()

# Tidspunktet akkurat nå
now = datetime.now()
print(f"{now = }")

# Tidspunkt minus tidspunkt gir varighet
time_since_lecture_started = now - lecture_starts
print(f"{time_since_lecture_started = }")

Et datetime-objekt kan være bevisst eller ubevisst på hvilken tidssone tidspunktet tilhører. I eksempel over var var vi ubevisst. I eksempelet under er vi bevisst på hvilken tidssone tidspunktene vi opererer med tilhører; vi må derfor angi hvilken tidssone tidspunktet tilhører når vi oppretter datetime-objekter.

from datetime import datetime, timedelta, timezone

# Tidspunkt i UTC (Universal Coordinated Time)
# UTC er den eneste tidssonen som er innbygget i Python fra før.
now_utc = datetime.now(timezone.utc)

# CEST (Central European Summer Time) er UTC+2
timezone_cest = timezone(timedelta(hours=2), name='CEST')
now_cest = datetime.now(timezone_cest)

print(f'{now_utc = }')
print(f'{now_cest = }')

# Forskjellen mellom to samtidige tidspunkt er 0.
difference = now_cest - now_utc
print(f'{difference = }')

# For å opprette bevisste datetime-objekter, må vi angi tzinfo-parameteren
lecture_time = datetime(2024, 4, 5, 12, 15, 00, tzinfo=timezone_cest)
print(f'{lecture_time = }')

from datetime import datetime, timedelta, timezone

# Ubevisst tidspunkt
lecture_time_unaware = datetime(2024, 3, 22, 12, 15, 00)
print(f'{str(lecture_time_unaware) = }')

# Konvertere fra ubevisst datetime til bevisst datetime, med bruk av
# den lokale tidssonen som var gjeldende den aktuelle datoen.
lecture_time_local = lecture_time_unaware.astimezone()
print(f'{str(lecture_time_local) = }')

# Noen tidssoner
tz_utc = timezone.utc # Universal Coordinated Time
tz_cet = timezone(timedelta(hours=1), name='CET') # Central European Time
tz_est = timezone(timedelta(hours=-5), name='EST') # Eastern Standard Time
tz_edt = timezone(timedelta(hours=-4), name='EDT') # Eastern Daylight Time
tz_pst = timezone(timedelta(hours=-8), name='PST') # Pacific Standard Time

# Konvertere til en annen tidssone kan gjøres på to måter:
#
#  new_datetime = old_datetime.replace(tzinfo=new_timezone)
#       endrer tidssonen uten å endre klokkeslettet/dato. For eksempel hvis
#       klokkeslettet var 12:15 i CET, vil det nye tidspunktet være 12:15 i
#       UTC.
# 
#  new_datetime = old_datetime.astimezone(new_timezone)
#       endrer tidssonen og klokkeslett/dato slik at det nye klokkeslettet
#      tilsvarer samme punkt i tid, bare oppgitt i en annen tidssone. For
#      eksempel hvis klokkeslettet var 12:15 i CET, vil det nye tidspunktet
#      være 10:15 i UTC.

lecture_time_replace_est = lecture_time_local.replace(tzinfo=tz_est)
print(f'{str(lecture_time_replace_est) = }')

lecture_time_astimezone_est = lecture_time_local.astimezone(tz_est)
print(f'{str(lecture_time_astimezone_est) = }')

# Dersom old_datetime er ubevvisst, vil både replace og astimezone fungere
# som replace, og vil da endre tidssonen uten å røre klokkeslett og dato.

csv

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

De enkleste CSV-filene er det lett å håndtere med bruk av .split og .join, slik vi viser i notatene om filer. Men for noen CSV-filer kan det bli komplisert: for eksempel når innholdet i en celle selv inneholder komma. Da er det bedre å bruke csv-modulen, som løser slike problemer for oss.

Når man tolker en CSV-fil er det noen parametre som er viktige å kjenne til:

CSV-modulen tillater at vi angir våre egne verdier for disse parametrene dersom de avviker fra standardverdiene.

import csv
# Du kan kopiere funksjonene for å lese/skrive csv-filer og bruke dem 
# som du ønsker uten å sitere.

def read_csv_file(path, encoding="utf-8", **kwargs):
    r''' Reads a csv file from the provided path, and returns its
    content as a 2D list. The default encoding is utf-8, the default
    column delimitier is comma and the default quote character is the
    double quote character ("), though this can be overridden with
    named parameters "delimiter" and "quotechar".'''
    with open(path, "rt", encoding=encoding, newline='') as f:
        return list(csv.reader(f, **kwargs))

def write_csv_file(path, table_content, encoding='utf-8', **kwargs):
    r""" Given a file path and a 2D list representing the content, this
    method will create a csv file with the contents formatted as csv.
    By defualt the delimiter is a comma and the quote character is
    the double quote, but this can be overridden with named parameters
     "delimiter" and "quotechar". """
    with open(path, "wt", encoding=encoding, newline='') as f:
        writer = csv.writer(f, **kwargs)
        for row in table_content:
            writer.writerow(row)

# Eksempeler på bruk. Først, en 2D-liste med innholdet i tabellen.
org_content = [
    ["Name", "Age"],
    ["Ola", 74],
    ['"Kari"', "73"],
]
print("Original 2D-liste:", org_content)

# Eksempel 1: standard parametre (se resultat i foo.csv)
write_csv_file("foo.csv", org_content)
with open("foo.csv", encoding='utf-8') as f:
    print("write_csv_file, standard parametre:", repr(f.read()))
readback_content = read_csv_file("foo.csv")
print("read_csv_file, standard parametre:", readback_content)

# Eksempel 2: eksempel på bruk av navngitte parametre (se resultat i bar.csv)
# delimiter="|" endrer skillesymbolet til vertikal strek
# quoting=csv.QUOTE_NONNUMERIC gjør at alt unntatt tall-verdier omsluttes
# av hermetegn; og ved lesing, at tall uten hermetegn konverteres til float.
write_csv_file("bar.csv", org_content, delimiter="|", 
                                       quoting=csv.QUOTE_NONNUMERIC)
with open("bar.csv", encoding='utf-8') as f:
    print("write_csv_file, med egne parametre:", repr(f.read()))
readback_content = read_csv_file("bar.csv", delimiter="|",
                                            quoting=csv.QUOTE_NONNUMERIC)
print("read_csv_file, med egne parametre:", readback_content)

CSV-bibliotektet har innbygd funksjonalitet for å konvertere mellom CSV-filer og lister av oppslagsverk (dict). Da benyttes csv.DictReader og csv.DictWriter i stedet for csv.reader og csv.writer.

import csv
from pathlib import Path

# Kopier gjerne funksjonene csv_dict_reader og csv_dict_writer herfra

def csv_dict_reader(path, encoding='utf-8', delimiter=',',
                    quotechar='"', quoting=csv.QUOTE_MINIMAL, **kwargs):
    '''Read a CSV file and return the headers as a list and the data as
    a list of dictionaries. Typical usage example:

        >>> headers, data = csv_dict_reader('foo.csv', delimiter=';')
        >>> headers
        ['Name', 'Age']
        >>> data
        [{'Name': 'Ola', 'Age': '74'}, {'Name': 'Kari', 'Age': '73'}]
    
    Args:
        path (str): The path to the CSV file.
        encoding (str, optional): The encoding of the CSV file. Default
            is 'utf-8'.
        delimiter (str, optional): The delimiter between cells used in
            the CSV file. Default is ','.
        quotechar (str, optional): The quote character used in the CSV
            file. Default is '"'.
        quoting (int, optional): The quoting style used in the CSV
            file. Default is csv.QUOTE_MINIMAL. Some other useful
            options are csv.QUOTE_ALL and csv.QUOTE_NONNUMERIC. See
            https://docs.python.org/3/library/csv.html#csv.QUOTE_ALL
            for more information.
        **kwargs: Additional keyword args to pass to csv.DictReader.

    Returns:
        headers (list): The headers of the CSV file.
        data (list): The data of the CSV file as a list of dictionaries.
    '''
    with Path(path).open('rt', encoding=encoding, newline='') as f:
        reader = csv.DictReader(f, delimiter=delimiter, quotechar=quotechar,
                                quoting=quoting, **kwargs)
        headers = reader.fieldnames
        data = list(reader)
    return headers, data

def csv_dict_writer(path, headers, data, encoding='utf-8', delimiter=',',
                    quotechar='"', quoting=csv.QUOTE_MINIMAL, **kwargs):
    '''Write a CSV file with the specified headers and data. Typical
    usage example:
    
        >>> headers = ['Name', 'Age']
        >>> data = [
        ...     {'Name': 'Ola', 'Age': 74},
        ...     {'Name': 'Kari', 'Age': 73},
        ... ]
        >>> csv_dict_writer('foo.csv', headers, data, delimiter=';')
            
    Args:
        path (str): The path to the CSV file.
        headers (list): The headers of the CSV file.
        data (list): The data of the CSV file as a list of dictionaries.
        encoding (str, optional): The encoding of the CSV file. Default
            is 'utf-8'.
        delimiter (str, optional): The delimiter between cells used in
            the CSV file. Default is ','.
        quotechar (str, optional): The quote character used in the CSV
            file. Default is '"'.
        quoting (int, optional): The quoting style used in the CSV
            file. Default is csv.QUOTE_MINIMAL. Some other useful
            options are csv.QUOTE_ALL and csv.QUOTE_NONNUMERIC. See
            https://docs.python.org/3/library/csv.html#csv.QUOTE_ALL
            for more information.
        **kwargs: Additional keyword args to pass to csv.DictWriter.

    Returns:
        None
    '''
    with Path(path).open('wt', encoding=encoding, newline='') as f:
        writer = csv.DictWriter(f, fieldnames=headers, delimiter=delimiter,
                                quotechar=quotechar, quoting=quoting)
        writer.writeheader()
        writer.writerows(data)

# Eksempel på bruk:
org_headers = ['Name', 'Age']
org_data = [
    {'Name': 'Ola', 'Age': 74},
    {'Name': 'Kari', 'Age': 73},
]

# Skriv til fil med ulike innstillinger med csv_dict_writer
csv_dict_writer('default.csv', org_headers, org_data)
csv_dict_writer('semicolon.csv', org_headers, org_data, delimiter=';')
csv_dict_writer('quotenonnumeric.csv', org_headers, org_data,
                quotechar="'", quoting=csv.QUOTE_NONNUMERIC)

# Les de produserte filene som rå tekst (plain text)
raw_csv_default = Path('default.csv').read_text(encoding='utf-8')
raw_csv_semicolon = Path('semicolon.csv').read_text(encoding='utf-8')
raw_csv_quotenumeric = Path('quotenonnumeric.csv').read_text(encoding='utf-8')

# Les de produserte filene tilbake med csv_dict_reader 
rb_default_headers, rb_default_data = csv_dict_reader('default.csv')
rb_semicolon_headers, rb_semicolon_data = csv_dict_reader(
    'semicolon.csv', delimiter=';'
)
rb_quotenumeric_headers, rb_quotenumeric_data = csv_dict_reader(
    'quotenonnumeric.csv', quotechar="'", quoting=csv.QUOTE_NONNUMERIC
)

print('Raw file content')
print('default:     ', repr(raw_csv_default))
print('semicolon:   ', repr(raw_csv_semicolon))
print('quotenumeric:', repr(raw_csv_quotenumeric))
# default:      'Name,Age\nOla,74\nKari,73\n'
# semicolon:    'Name;Age\nOla;74\nKari;73\n'
# quotenumeric: "'Name','Age'\n'Ola',74\n'Kari',73\n"

print()
print('Readback headers')
print('default:     ', rb_default_headers)
print('semicolon:   ', rb_semicolon_headers)
print('quotenumeric:', rb_quotenumeric_headers)
# rb_default:      ['Name', 'Age']
# rb_semicolon:    ['Name', 'Age']
# rb_quotenumeric: ['Name', 'Age']

print()
print('Readback data')
print('default:     ', rb_default_data)
print('semicolon:   ', rb_semicolon_data)
print('quotenumeric:', rb_quotenumeric_data)
# default:      [{'Name': 'Ola', 'Age': '74'}, {'Name': 'Kari', 'Age': '73'}]
# semicolon:    [{'Name': 'Ola', 'Age': '74'}, {'Name': 'Kari', 'Age': '73'}]
# quotenumeric: [{'Name': 'Ola', 'Age': 74.0}, {'Name': 'Kari', 'Age': 73.0}]

Å benytte DictReader med strenger i stedet for filer er dessverre litt knotete. Vi må en liten omvei via io.StringIO fra io-modulen i standardbiblioteket.

import csv
import io

csv_string = '''\
Name;Age
Ola;74
Kari;73
'''

# Leser fra en streng med DictReader
reader = csv.DictReader(io.StringIO(csv_string), delimiter=';')
headers = reader.fieldnames
data = list(reader)

print(headers) # ['Name', 'Age']
print(data)  # [{'Name': 'Ola', 'Age': '74'}, {'Name': 'Kari', 'Age': '73'}]

json

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

JSON er et filformat basert på ren tekst som i sin struktur er nesten nøyaktig som et oppslagsverk hvor alle nøklene er strenger. På samme måte som for CSV er det enkelte detaljer som gjør at import og eksport av JSON likevel gjøres best med en egnet modul.

import json

# Eksempel på innholdet i en JSON-fil som en streng (såkalt JSON-streng)
sample_json_string = """\
{
  "name": "Kari",
  "is_alive": true,
  "age": 27,
  "address": {
    "street": "Gateveien 1234",
    "city": "En 'by'",
    "postal_code": "5000"
  },
  "phone_numbers": [
    {
      "type": "home",
      "number": "12345678"
    },
    {
      "type": "office",
      "number": "23456789"
    }
  ]
}
"""

# For å konvertere fra JSON-streng til oppslagsverk
d = json.loads(sample_json_string)
print(type(d)) # dict
print(d["name"], "har telefonnummer", d["phone_numbers"][0]["number"])
print()

# For å konvertere fra oppslagsverk til JSON-streng
d["age"] += 42 # Liten endring først
s1 = json.dumps(d) # Kompakt JSON-streng, bra for nedlasting/datamaskiner
s2 = json.dumps(d, indent=2) # Pen og leselig JSON-streng, bra for mennesker
print(s1)
print(str(d)) # Legg merke til at s1 ligner på str(d). Ser du forskjellene?
print(s2)
sys

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

import sys

# Avslutt python umiddelbart
sys.exit()
print("Vi kommer aldri hit")
os og shutil

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

https://docs.python.org/3/library/os.path.html

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

os er en modul med mange avanserte funksjoner, men også et par funksjoner som er greie å ha for å navigere filsystemet lokalt på datamaskinen. shutil er en modul som også jobber med filer, gjerne mer enn én om gangen.

For å testekoden under, lim den inn i en fil og kjør filen på din lokale maskin.

import os
import shutil
# os er en modul med mange avanserte funksjoner, men også et par funksjoner
# som er greie å ha for å navigere filsystemet lokalt på datamaskinen.
# shutil er en modul som også jobber med filer, gjerne mer enn én om gangen.

# Vis `current working directory` (cwd). Dette er den mappen python
# vil bruke som utgangspunkt for å se etter filstier.
full_folder_path = os.getcwd() # en streng
print("Current working directory er:\n", full_folder_path) 

print("Oppretter nå en fil foo.txt med innhold: 'woof\nbark\n'")
with open("foo.txt", "wt", encoding='utf-8') as f:
    f.write("woof\nbark\n")
print("Foo.txt ble opprettet i mappen:\n", os.getcwd())

# Komplett filsti for filen foo.txt vi nettopp opprettet. Dette er bedre enn
# `os.getcwd() + "/foo.txt"` fordi det virker både for Windows og Mac.
full_file_path = os.path.join(os.getcwd(), "foo.txt")
print("Filsti (path) for foo.txt:", full_file_path)
print("Sjekk at filen foo.txt ble opprettet i denne mappen")
input("Trykk enter for å fortsette")
print()

print("Oppretter nå mappen bar inne i en mappen egg, inne i en mappen temp")
os.makedirs(os.path.join("temp", "egg", "bar"))
print("Sjekk at mappene temp/egg/bar ble opprettet i ", os.getcwd())
input("Trykk enter for å fortsette")
print()

def print_contents_of_folder(path_to_folder):
    print("Ser på alle ting i mappen", path_to_folder)
    # os.listdir returnerer en liste med strenger
    for subpath in sorted(os.listdir(path_to_folder)):
        # subpath er navnet til fil/mappe som er i mappen folder_path
        full_subpath = os.path.join(path_to_folder, subpath)
        subpath_is_file = os.path.isfile(full_subpath)
        print("file    " if subpath_is_file else "folder  ", end="")
        print(subpath)

print_contents_of_folder(full_folder_path)
input("Trykk enter for å fortsette")
print()

print("Endrer cwd (current working directory) til mappen temp/egg/bar")
os.chdir(os.path.join(full_folder_path, "temp", "egg", "bar"))
print("Nå er os.getcwd() =", os.getcwd())
print("Oppretter filer xi.txt, tau.txt, alpha.txt")
for filename in ["xi.txt", "tau.txt", "alpha.txt"]:
    with open(filename, "wt", encoding='utf-8') as f:
        f.write("hiha")
print_contents_of_folder(os.getcwd())
input("Trykk enter for å fortsette")
print()

print("Endrer cwd ved å gå til mappen på nivået over tre ganger")
for _ in range(3):
    os.chdir(os.path.dirname(os.getcwd()))
    print("cwd er nå", os.getcwd())
input("Trykk enter for å fortsette")
print()

temp_folder_path = os.path.join(os.getcwd(), "temp")
print("Sjekker om stien eksisterer:", temp_folder_path)
print(os.path.exists(temp_folder_path))
print("Går igjennom alt innhold uansett dypbe:", temp_folder_path)
for dirpath, dirnames, filnames in os.walk(temp_folder_path):
    print(dirpath, dirnames, filnames)
input("Trykk enter for å fortsette")
print()

os.remove(full_file_path)
print("Fjernet foo.txt")
input("Trykk enter for å fortsette")
print()

import shutil
shutil.rmtree(temp_folder_path)
print("Fjernet temp-mappen inkludert alt innhold")
print("Ferdig!")