Ännu en onsdag, och med det ett nytt avsnitt av…

Introduktion till programmering

Förra veckan tittade vi på Objekt, och vi ska spinna vidare lite på det spåret och titta på andra avancerade datatyper.

Ett objekt kommunicerar man med genom metoder, men i praktiken kan man säga att alla avancerade datatyper är en form av objekt även om de inte har några metoder.

I förra inlägget nämnde jag datetime-objektet i Python. Det är ett bra exempel på ett objekt, så jag tänkte att vi ska gå igenom hur vi skulle kunna göra vårt eget!

Först definierar vi attributen:

År, månad, dag, timme, minut, sekund – alla kan lagras som heltal

Och några förslag på metoder:

“lagra nuvarande tid” – hämta datum och klockslag från systemet och lagra dessa i objektet.

“lagra <attribut>” – sätt ett av attributen ovan

“hämta <attribut>” – hämta ett av attributen ovan

“hämta datetime” – returnerar en sträng som representerar hela objektet (såväl datum som klockslag).

“hämta månad som sträng” – returnerar en sträng som motsvarar namnet på månaden, t.ex. “Januari”.

 

Det mesta här är relativt enkelt. Det handlar om getters och setters, som vi sett tidigare. Det som blir lite mer invecklat är “lagra nuvarande tid”, “hämta datetime” och “hämta månad som sträng” eftersom dessa behöver byggas upp av beräkningar.

Källkod för de enkla bitarna:

class Datumtid:
# Konstruktor, tom än så länge men om man anropar den med "true" ska den kunna hämta in och lagra nuvarande tid från systemet.
 def __init__(self, nu=false):
  #if nu:  <--------------------------- Lägg till senare

# SET-metoder
 def setYear(self, tal):
  self._year = int(tal)
 def setMonth(self, tal):
  self._month = int(tal)
 def setDay(self, tal):
  self._day = int(tal)
 def setHour(self, tal):
  self._hour = int(tal)
 def setMinute(self, tal):
  self._minute = int(tal)
 def setSecond(self, tal):
  self._second = int(tal)

# GET-metoder
 def getYear(self):
  return self._year
 def getMonth(self):
  return self._month
 def getDay(self):
  return self._day
 def getHour(self):
  return self._hour
 def getMinute(self):
  return self._minute
 def getSecond(self):
  return self._second

# Get-metod, string
 def __str__(self):
  datum = str(self._year)+"-"+str(self._month)+"-"+str(self._day)
  tid = str(self._hour)+":"+str(self._minute)+":"+str(self._day)
  return datum+", "+tid

Nu har vi inga kollar över huvud taget för att kolla att man anger rätt data. År kan vi bortse från, men övriga set-funktioner bör vara begränsade som följer:

Månad (1-12), dag (1-31), timme (<24), minut (<60), sekund (<60). Vi lägger till detta i nästa steg (med något som heter “range”). Nu vill vi skriva en funktion för att hämta in nuvarande tid och lagra i vårt objekt.

För att lösa detta problem kan man göra på olika sätt. Det går att hämta “antalet millisekunder sedan 1970-01-01 kl 00:00:00:000”. Men gör vi det måste vi konvertera ett väldigt stort tal till en stor mängd små tal, vilket är lite bökigt. Särskilt eftersom detta problem redan har lösts smart i Pythons befintliga datetime-objekt! Så vi fuskar lite istället. Vi importerar “datetime”, och anropar datetime.now(), och får då ett datetime-objekt. Detta omvandlar vi sedan för att få en lista innehållande de heltal vi vill ha:

import datetime

date = datetime.datetime.now()
datelist = list(date.timetuple())

print(str(datelist))

Koden ovan skriver ut en lista, t.ex.: [2014, 11, 5, 10, 16, 41, 2, 309, -1]

De värden vi vill ha är de sex första (se beskrivning av datetime-objektet om du vill veta vad de övriga gör).

Nu ska vi lägga in dessa beräkningar i konstruktorn, för att man ska kunna sätta vårt DatumTid-objekt till nuvarande tidpunkt när det skapas:

 def __init__(self, nu=false):
  if nu:
   date = datetime.datetime.now()
   datelist = list(date.timetuple())
   this._year = datelist[0]
   this._month = datelist[1]
   this._day = datelist[2]
   this._hour = datelist[3]
   this._minute = datelist[4]
   this._second = datelist[5]

Om man nu skapar ett DatumTid-objekt, t.ex. genom att skriva “temp = DatumTid(True)” kommer nuvarande tid hämtas från systemet och lagras i objektet.

Nu har vi ett problem kvar! Vi vill kunna skriva ut månadsnamnet istället för motsvarande siffra. För att göra detta tänkte vi använda oss av en avancerad datatyp, en “hash-tabell”. I Python kallas dessa “Dictionaries”.

Dictionaries

En Dictionary är, enkelt uttryckt, en lista med två värden. Ett sökvärde, och ett datavärde. Om vi döper vår Dictionary till “dict” hämtas datan ut genom att skriva “dict[sökvärde]”.

Vad är då fördelen med Dictionaries/hash-tabeller? Jo, säg att man har en stor mängd data som man vill lagra, och kunna söka snabbt smidigt. Att lägga all denna data i en lång lista är inte så effektivt om man letar efter ett specifikt värde, för om datan t.ex. ligger sist i listan måste man traversera hela listan innan man hittar det.

Det man då kan göra är att skapa en hashfunktion, som på ett eller annat sätt tar en värde från datamängden som invärde och returnerar en nyckel. Om man vill kan man ha mer invecklade nyckelvärden, som i exemplet på hashfunktions-sidan jag länkade, kan man såklart göra så. Men man kan också ha enklare hashnycklar, och de värden vars data råkar ge samma nyckel kan ligga i samma underlista:

“a” –> hash(“a”) –> 1  –> 1 – “a”

“b” –> hash(“b”) –> 1 –> 1 – “a”, “b”

Gör man så här får man fortfarande traversera en lista, men säg att vi hashar hela alfabetet och de övriga bokstäverna får hashnyckel “2”, så blir det lite smidigare beräkningsmässigt att hitta den data man söker.

I vårt fall behöver vi dock inte skapa någon hash-funktion eftersom vi redan vet vilka nycklar vi har och vilken data vi vill ha ut! Därför skapar vi helt enkelt en dictionary som innehåller datan:

import datetime

class Datumtid:

 _months = {
 1 : "Januari",
 2 : "Februari",
 3 : "Mars",
 4 : "April",
 5 : "Maj",
 6 : "Juni",
 7 : "Juli",
 8 : "Augusti",
 9 : "September",
 10 : "Oktober",
 11 : "November",
 12 : "December" }
 
# Konstruktor, om true, lagra nuvarande tid
 def __init__(self, nu=False):
  if nu:
   date = datetime.datetime.now()
   datelist = list(date.timetuple())
   self._year = datelist[0]
   self._month = datelist[1]
   self._day = datelist[2]
   self._hour = datelist[3]
   self._minute = datelist[4]
   self._second = datelist[5]

# SET-metoder
 def setYear(self, tal):
  self._year = int(tal)
 def setMonth(self, tal):
  self._month = int(tal)
 def setDay(self, tal):
  self._day = int(tal)
 def setHour(self, tal):
  self._hour = int(tal)
 def setMinute(self, tal):
  self._minute = int(tal)
 def setSecond(self, tal):
  self._second = int(tal)

# GET-metoder
 def getYear(self):
  return self._year
 def getMonth(self):
  return self._month
 def getDay(self):
  return self._day
 def getHour(self):
  return self._hour
 def getMinute(self):
  return self._minute
 def getSecond(self):
  return self._second

# Get-metod, string
 def __str__(self):
  datum = str(self._day)+" "+self._months[self._month]+" "+str(self._year)
  tid = str(self._hour)+":"+str(self._minute)+":"+str(self._day)
  return datum+", kl "+tid

 

_months[1] returnerar nu “Januari”. Precis vad vi behövde! Den placeras högst upp i klassen, då det är ett attribut som ska kunna kommas åt från metoderna. I exemplet ovan har jag också justerat utskriftsfunktionen för att skriva ut månadsnamnet istället.

Nu har vi skrivit ett eget, mer invecklat objekt och använt oss av en avancerad datatyp, Dictionary!  Vi har också lärt oss hur man definierar hur ett objekt skrivs ut som sträng (__str__(self) ).

Nästa vecka spinner vi vidare lite på ämnet sökning, och tittar på sorteringsfunktioner! Håll ut 😉