Eksamen: REA3049-PY | Semester: Høst 2024 | Tema: OOP, palindrom-test, batteri-klasse, velferdsteknologi, befolkningsdatasett, virusspredning
Arv (inheritance) er nettopp mekanismen som gjør at en subklasse arver egenskaper og metoder fra en superklasse — ren gjenbruk uten å skrive koden om igjen. Abstraksjon handler om å skjule kompleksitet, polymorfisme om at samme kall gir ulik oppførsel per type, og innkapsling om å skille grensesnitt fra interne detaljer.
Når objektet «eier» de andre objektene som felt, og de eide objektene normalt ikke eksisterer uten den ytre helheten, er det komposisjon. Aggregering er svakere (delene kan eksistere alene); generalisering er arv; assosiasjon er en løs «vet om»-relasjon.
beregn(7)?| i | Partall? | Operasjon | s etter |
|---|---|---|---|
| 1 | Nei | s − 1 | −1 |
| 2 | Ja | s + 2 | 1 |
| 3 | Nei | s − 3 | −2 |
| 4 | Ja | s + 4 | 2 |
| 5 | Nei | s − 5 | −3 |
| 6 | Ja | s + 6 | 3 |
| 7 | Nei | s − 7 | −4 |
IF temperatur GREATER THAN 25 DISPLAY "Det er varmt." ELSE IF temperatur GREATER THAN OR EQUAL TO 10 DISPLAY "Det er mildt." ELSE IF temperatur GREATER THAN OR EQUAL TO 0 DISPLAY "Det er kjølig." ELSE DISPLAY "Det er kaldt." ENDIF
Strukturen er en kjede av if/else-if/else fra høyeste til laveste terskel, med én avsluttende ENDIF. Rekkefølgen er kritisk: hvis vi sjekker «≥ 0» før «> 25», vil 30-grader-tilfellet feilaktig matche «kjølig».
Algoritmen leser et positivt heltall h, lagrer originalen i t, og bygger opp den reverserte versjonen av tallet i s ved å plukke siste siffer (h % 10), legge det til som ny siste siffer i s (s * 10 + r) og fjerne sist siffer fra h (h // 10). Til slutt sammenlignes original (t) med revers (s) — er de like, er tallet et palindrom (leses likt forfra og bakfra). Eksempel: 121 reverseres til 121 → True. 123 reverseres til 321 → False.
# oppgave5b.py — antall tresifrede palindromer
def er_palindrom(h: int) -> bool:
t, s = h, 0
while h != 0:
s = s * 10 + h % 10
h //= 10
return t == s
def tell_tresifrede_palindromer() -> int:
return sum(1 for n in range(100, 1000) if er_palindrom(n))
if __name__ == "__main__":
antall = tell_tresifrede_palindromer()
print(f"Antall tresifrede palindromer: {antall}")
# Forventet svar: 90 (formel: 9 * 10 — første siffer 1-9, midt-siffer 0-9, siste = første)
Resultat: 90 tresifrede palindromer (101, 111, 121, …, 191, 202, …, 999). Matematisk: 9 valg for første/siste siffer × 10 valg for midten = 90.
# batteri.py — energilager med privat tilstand
class BatteriFeil(Exception):
"""Egendefinert feil for ulovlige batteri-operasjoner."""
class Batteri:
def __init__(self, kapasitet: float, start_niva: float = 0) -> None:
if kapasitet <= 0:
raise BatteriFeil("Kapasiteten må være positiv.")
if not (0 <= start_niva <= kapasitet):
raise BatteriFeil(f"Startnivå ({start_niva}) må være mellom 0 og {kapasitet}.")
self._kapasitet = kapasitet
self._energinivaa = start_niva
def lad(self, energi: float) -> None:
if energi < 0:
raise BatteriFeil("Kan ikke lade med negativ energi.")
ny = self._energinivaa + energi
# Cap til kapasitet (fysisk realistisk oppførsel)
self._energinivaa = min(ny, self._kapasitet)
def bruk(self, energi: float) -> None:
if energi < 0:
raise BatteriFeil("Kan ikke bruke negativ energi.")
if energi > self._energinivaa:
raise BatteriFeil(
f"Ikke nok energi: forsøkt {energi}, tilgjengelig {self._energinivaa}."
)
self._energinivaa -= energi
def vis_status(self) -> None:
prosent = self._energinivaa / self._kapasitet * 100
print(f"Batteri: {self._energinivaa:.1f} / {self._kapasitet:.1f} kWh ({prosent:.0f} %)")
def test_batteri() -> None:
b = Batteri(kapasitet=10.0, start_niva=5.0)
b.vis_status() # 5.0 / 10.0 (50%)
b.lad(3.0)
b.vis_status() # 8.0 / 10.0 (80%)
b.lad(5.0) # cap til 10
b.vis_status() # 10.0 / 10.0 (100%)
b.bruk(7.5)
b.vis_status() # 2.5 / 10.0 (25%)
# Feil 1: Bruke mer energi enn det er igjen
try:
b.bruk(10.0)
except BatteriFeil as e:
print(f"OK – fanget: {e}")
# Feil 2: Lade med negativ energi
try:
b.lad(-1.0)
except BatteriFeil as e:
print(f"OK – fanget: {e}")
# Unntak: Ugyldig start ved opprettelse
try:
Batteri(kapasitet=-5.0)
except BatteriFeil as e:
print(f"OK – fanget: {e}")
print("Alle tester passert.")
if __name__ == "__main__":
test_batteri()
Identifiserte feil/unntak: (1) Bruk av mer energi enn tilgjengelig. (2) Negativ ladnings- eller bruksverdi. (Unntak) Ugyldige verdier ved opprettelse (negativ kapasitet eller startnivå utenfor 0..kapasitet). Alle håndtert ved å kaste BatteriFeil. Lading utover kapasitet håndteres ved «capping» til full kapasitet — det matcher hvordan ekte batterier oppfører seg.
Velferdsteknologi — fallsensorer, GPS-armbånd, automatiske medisindispensere, robotkjæledyr og videokonsultasjon — gir både muligheter og dilemmaer i eldreomsorgen.
Fordeler. Teknologien kan øke selvstendigheten og tryggheten til eldre som bor hjemme. Fallsensorer varsler hjelp raskt, GPS-løsninger lar personer med demens bevege seg utendørs uten konstant tilsyn, og videosamtaler reduserer ensomhet. Effektivt brukt frigjør teknologien tid for helsepersonell til de oppgavene som krever menneskelig nærvær.
Etiske dilemmaer.
Konklusjon. Velferdsteknologi bør innføres etter prinsippet «teknologi som verktøy, ikke erstatning». Den krever solid juridisk rammeverk for samtykke og databehandling, opplæring av både brukere og pleiere, og en bevisst arbeidsdeling der teknologien tar de rutinepregede oppgavene mens menneskene står for den emosjonelle og verdiladede kontakten.
# oppgave9.py — befolkningsutvikling 1945–2024
"""Forberedelse av datasettet:
- Datasettet inneholder årlige tall for fødsler, innflyttinger og utflyttinger.
- Vi konverterer numeriske kolonner til int (fjerner mellomrom som tusenskille).
- Netto folkevekst beregnes som fødsler + innflyttinger − utflyttinger."""
import csv
import matplotlib.pyplot as plt
from pathlib import Path
DATAFIL = Path("befolkning.csv")
def les_data(filsti: Path = DATAFIL) -> list[dict]:
with filsti.open(encoding="utf-8") as f:
rader = list(csv.DictReader(f, delimiter=";"))
for r in rader:
r["år"] = int(r["år"])
for kol in ("fødselstall", "innflyttinger", "utflyttinger"):
r[kol] = int(r[kol].replace(" ", ""))
r["netto folkevekst"] = r["fødselstall"] + r["innflyttinger"] - r["utflyttinger"]
return rader
def vis_tabell(data: list[dict]) -> None:
print(f"\n{'År':<6}{'Fødte':>10}{'Inn':>10}{'Ut':>10}{'Netto':>12}")
print("-" * 48)
for r in data:
print(
f"{r['år']:<6}{r['fødselstall']:>10,}{r['innflyttinger']:>10,}"
f"{r['utflyttinger']:>10,}{r['netto folkevekst']:>12,}".replace(",", " ")
)
def tegn_diagram(data: list[dict], kolonne: str, fra: int, til: int) -> None:
valgte = [r for r in data if fra <= r["år"] <= til]
if not valgte:
print("Ingen data i valgt periode.")
return
aar = [r["år"] for r in valgte]
verdier = [r[kolonne] for r in valgte]
plt.figure(figsize=(10, 5))
plt.plot(aar, verdier, marker="o", color="#1976D2")
plt.title(f"{kolonne.capitalize()} {fra}–{til}")
plt.xlabel("År")
plt.ylabel("Antall personer")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(f"diagram_{kolonne.replace(' ', '_')}_{fra}_{til}.png", dpi=120)
plt.show()
def main() -> None:
data = les_data()
vis_tabell(data)
valg = input("\nKolonne (fødselstall / innflyttinger / utflyttinger / netto folkevekst): ").strip().lower()
if valg not in {"fødselstall", "innflyttinger", "utflyttinger", "netto folkevekst"}:
print("Ugyldig kolonne.")
return
try:
fra = int(input("Fra år (1945–2024): "))
til = int(input("Til år: "))
except ValueError:
print("Ugyldige år.")
return
tegn_diagram(data, valg, fra, til)
if __name__ == "__main__":
main()
# virusspredning.py — fullstendig SIR-lignende simulering
from __future__ import annotations
import random
import tkinter as tk
from enum import Enum
DAGER_SMITTET = 3
DAGER_SYK = 4
DAGLIG_DODSSANNSYNLIGHET = 0.01
SMITTE_RADIUS = 2 # celler unna
SMITTE_SANNSYNLIGHET = 0.3
class Tilstand(Enum):
FRISK = "frisk"
SMITTET = "smittet"
SYK = "syk"
IMMUN = "immun"
DOD = "død"
class Person:
def __init__(self, rad: int, kol: int) -> None:
self.rad = rad
self.kol = kol
self.tilstand = Tilstand.FRISK
self.dager_i_tilstand = 0
def smitt(self) -> None:
"""Bare frisk uten immunitet kan smittes."""
if self.tilstand == Tilstand.FRISK:
self.tilstand = Tilstand.SMITTET
self.dager_i_tilstand = 0
def neste_dag(self) -> None:
if self.tilstand in (Tilstand.FRISK, Tilstand.IMMUN, Tilstand.DOD):
return
self.dager_i_tilstand += 1
if self.tilstand == Tilstand.SMITTET and self.dager_i_tilstand >= DAGER_SMITTET:
self.tilstand = Tilstand.SYK
self.dager_i_tilstand = 0
return
if self.tilstand == Tilstand.SYK:
if random.random() < DAGLIG_DODSSANNSYNLIGHET:
self.tilstand = Tilstand.DOD
return
if self.dager_i_tilstand >= DAGER_SYK:
self.tilstand = Tilstand.IMMUN
self.dager_i_tilstand = 0
class Populasjon:
def __init__(self, rader: int, kolonner: int, start_smittede: int = 3) -> None:
self.rader = rader
self.kolonner = kolonner
self.personer: list[list[Person]] = [
[Person(r, k) for k in range(kolonner)]
for r in range(rader)
]
for _ in range(start_smittede):
self.personer[random.randrange(rader)][random.randrange(kolonner)].smitt()
def _naboer(self, p: Person) -> list[Person]:
return [
self.personer[r][k]
for r in range(max(0, p.rad - SMITTE_RADIUS), min(self.rader, p.rad + SMITTE_RADIUS + 1))
for k in range(max(0, p.kol - SMITTE_RADIUS), min(self.kolonner, p.kol + SMITTE_RADIUS + 1))
if (r, k) != (p.rad, p.kol)
]
def en_dag(self) -> None:
# 1) Smitt nye basert på smittebærere FØR de oppdateres
smittebarere = [
p for rad in self.personer for p in rad
if p.tilstand in (Tilstand.SMITTET, Tilstand.SYK)
]
for p in smittebarere:
for nabo in self._naboer(p):
if random.random() < SMITTE_SANNSYNLIGHET:
nabo.smitt()
# 2) Oppdater alle individer
for rad in self.personer:
for p in rad:
p.neste_dag()
def statistikk(self) -> dict[Tilstand, int]:
teller: dict[Tilstand, int] = {t: 0 for t in Tilstand}
for rad in self.personer:
for p in rad:
teller[p.tilstand] += 1
return teller
CELLE_PX = 14
TICK_MS = 200
FARGER = {
Tilstand.FRISK: "#cccccc",
Tilstand.SMITTET: "#f48fb1",
Tilstand.SYK: "#e53935",
Tilstand.IMMUN: "#424242",
Tilstand.DOD: "#000000",
}
class Simulering:
def __init__(self, populasjon: Populasjon) -> None:
self.pop = populasjon
self.dag = 0
self.root = tk.Tk()
self.root.title("Virusspredning")
self.canvas = tk.Canvas(
self.root,
width=populasjon.kolonner * CELLE_PX,
height=populasjon.rader * CELLE_PX,
bg="white", highlightthickness=0,
)
self.canvas.pack()
self.status = tk.Label(self.root, text="", font=("Helvetica", 11))
self.status.pack(pady=4)
self.celleids: dict[tuple[int, int], int] = {}
self._tegn_init()
def _tegn_init(self) -> None:
for rad in self.pop.personer:
for p in rad:
x1, y1 = p.kol * CELLE_PX, p.rad * CELLE_PX
rid = self.canvas.create_rectangle(
x1, y1, x1 + CELLE_PX, y1 + CELLE_PX,
fill=FARGER[p.tilstand], outline="",
)
self.celleids[(p.rad, p.kol)] = rid
self._oppdater_status()
def _oppdater_status(self) -> None:
s = self.pop.statistikk()
self.status.config(
text=f"Dag {self.dag} | Frisk {s[Tilstand.FRISK]} Smittet {s[Tilstand.SMITTET]} "
f"Syk {s[Tilstand.SYK]} Immun {s[Tilstand.IMMUN]} Død {s[Tilstand.DOD]}"
)
def _steg(self) -> None:
self.pop.en_dag()
self.dag += 1
for rad in self.pop.personer:
for p in rad:
self.canvas.itemconfig(self.celleids[(p.rad, p.kol)], fill=FARGER[p.tilstand])
self._oppdater_status()
self.root.after(TICK_MS, self._steg)
def kjor(self) -> None:
self.root.after(TICK_MS, self._steg)
self.root.mainloop()
if __name__ == "__main__":
Simulering(Populasjon(rader=40, kolonner=40, start_smittede=5)).kjor()
Person (Del 1 — tilstandsmaskin), Populasjon (Del 2 — smittedynamikk), Simulering (Del 3 — Tkinter-visning).123456/
├── oppgave5b/
│ └── palindrom.py
├── oppgave6/
│ └── batteri.py
├── oppgave9/
│ ├── oppgave9.py
│ └── befolkning.csv
├── oppgave10/
│ └── virusspredning.py
└── README.md