It og informationssøgning Forelæsning 7 25. oktober 2006 Jakob Grue Simonsen Klasser Downey, Elkner & Meyers: Chapt. 12-14 Andersen & Simonsen: kap. 7 Vi har tidligere set, at Python tilbyder en række indbyggede typer (heltal, tal med flydende komma, tekster, lister etc.). Python tilbyder programmøren at definere sine egne typer, specielt sammensatte typer. Fordel: Alle operationer, der naturligt hører til en type kan samles på et sted (i programteksten). De strukturer, der således kan defineres kaldes klasser ; et element i en klasse kaldes et objekt (el. en instans af klassen). 1
Klassedefinitioner Vi benytter lærebogens eksempel om punkter i planen (tænk GPS-koordinater el. Kraks kort). En klassedefinition kunne se således ud: class Point: pass Altså: Det reserverede ord class efterfulgt af et navn på klassen og en ikke-tom krop ( indholdet af klassen). I ovenstående er kroppen semantisk tom : Den må ikke være tom, så vi har blot tilføjet det reserverede ord pass, der intet gør. 2
En klasse instantieres ved at kalde en konstruktør (som er en funktion, der heldigvis har samme navn som klassen selv): >>> tom = Point() >>> tom1 = Point() tom og tom1 er nu (forskellige!) objekter af klassen Point (bemærk nedenfor, at Pythons interne adresserepræsentationer for de to objekter er forskellige) >>> tom < main.point instance at 0xb7c5258c> >>> tom1 < main.point instance at 0xb7c523cc> Yderligere information attributter kan indeholdes i et objekt ved brug af punktum: >>> tom.x = 3.0 >>> tom.y = 4.1 >>> tom.x 3.0 Bemærk i ovenstående, at denne information også tilgås ved brug af punktum. >>> print "(" + str(tom.x) + "," + str(tom.y) +")" (3.0,4.1) 3
Instanser af klasser som parametre Instanser (men ikke klasser selv) kan benyttes som parametre til funktioner i Python: def printpoint(p): print "(" + str(tom.x) + "," + str(tom.y) +")" Yderligere eksempel: >>> class Person:... fornavn = ""... efternavn = ""... >>> person1 = Person() >>> person1.fornavn = "Theophilus" >>> person1.efternavn = "Balubbenberg" >>> def efteraendr(tekst,person):... person.efternavn = tekst... >>> efteraendr("nielsen",person1) >>> person1.efternavn Nielsen 4
Yderligere eksempel: def find_i_efternavn(person,tegn): i = 0 leng = len(person.efternavn) while i < leng: if person.efternavn[i] == tegn: return i i = i+1 return -1 >>> print person1.efternavn Nielsen >>> find_i_efternavn(person1,"l") 3 >>> find_i_efternavn(person1,"k") -1 5
Instanser som returværdier Ligeledes kan instanser af en klasse være returværdier ( det, som funktioner giver tilbage ): def returner_person(efternavn): p = Person() p.fornavn = "Theophilus" p.efternavn = efternavn return p >>> returner_person("frege") < main.person instance at 0xb7c527ac> 6
Kopiering og aliasing Kom ihu, fra forelæsningen om lister, at fænomenet aliasing kan forekomme: >>> a = [1,2,3] >>> b = a >>> b[0] = 45 >>> print a [45, 2, 3] Samme fænomen forekommer med instanser af klasser; vi ønsker at undgå det ved metoder til at kopiere instanser. Til dette formål haves copy-modulet: >>> import copy >>> p1 = Point() >>> p1.x = 3.0 >>> p1.y = 4.1 >>> p2 = copy.copy(p1) >>> p1 == p2 False >>> print p2.x 3.0 >>> print p2.y 4.1 Bemærk, at der ikke forekommer aliasing i ovenstående! 7
Kopiering og aliasing (fortsat) Kopieringen på foregående transparent kaldes flad kopiering den virker glimrende, hvis instanserne ikke indeholder andre objekter (instanser af andre klasser). Men I kopiering af: class person_og_sted: pass p = person_og_sted() p.navn = "Theophilus" p.lokation = Point() p.lokation.x = 3.0 p.lokation.y = 4.1 Er det ikke tilstrækkeligt, idet den indlejrede instans af klassen Point ikke bliver kopieret ved kald af copy(p), så aliasing vil forekomme. Såkaldt dyb kopiering løser problemet ved at kopiere alle indeholde instanser (og kopiere deres indeholdte intanser, etc.) p2 = copy.deepcopy(p) 8
Rene og urene funktioner En funktion siges at være ren, hvis den ikke ændrer objekter, den får som parametre eller har såkaldte bivirkninger (ændringer i maskinens tilstand, som f.eks. lageropdatering eller udskrift til brugeren). Et eksempel er: def f(p): return p.x Et eksempel på en uren funktion er: def f(p,y): p.x = y print p.x En særlig type urene funktioner er de såkaldte modifikatorer, der ændrer de objekter, de får ind i ovenstående ændrede vi f.eks. p.x. Havde vi fjernet denne linje, havde vi stadig haft en uren funktion, men den havde ikke været en modifikator. 9
Metoder Vi har hidtil set, hvordan klasser (og instanser deraf) kan have attributter. Vi vil gerne lægge mere avancerede konstruktioner deri specielt vil vi gerne lægge funktioner ind. Funktioner, der hører til klasser kaldes metoder. Eksempel: class Person2: navn = "" def printnavn(self): print self.navn >>> p = Person2() >>> p.navn = "Theophilus" >>> p.printnavn() Theophilus Bemærk brugen af self dette reserverede ord henviser til instansen selv. I definitionen af metoden printnavn skal self medtages som argument thi i kaldet p.printnavn tages p automatisk selv med som parameter. 10
Valgfri parametre Flere af de indbyggede funktioner i Python har valgfri argumenter vi kan i et kald af en funktion udelade et eller flere argumenter. Vi kan selv tilsvarende definere funktioner med valgfri argumenter (hvilket skal vise sig nyttigt, når såkaldte initialiseringsmetoder skal defineres): Eksempel: Gennemløb af en liste ls for at finde element l: def findlist(ls,l,start=0): i = start while i < len(ls): if ls[i] == l: return i i = i + 1 return -1 >>> findlist([1,4,6],5) -1 >>> findlist([1,4,6],1) 0 >>> findlist([1,4,6],1,1) -1 11
Initialiseringsmetoder Vi har se, hvordan ny objekter (altså: instanser af klasser) skabes ved at kalde en funktion med samme navn som klassen. Imidlertid skaber dette en rå instans, der ikke nødvendigvis har den starttilstand, som vi gerne vil have. Vi ønsker en særegen metode til dette; Python stiller den til rådighed under navnet init. Eksempel: class Person: def init (self,fornavn="theophilus",efternavn="balubbenberg"): self.fornavn = fornavn self.efternavn = efternavn >>> p = Person("Gottlob","Frege") >>> p.fornavn Gottlob >>> p.efternavn Frege >>> p1 = Person(efternavn="Frege") >>> p1.fornavn Theophilus >>> p1.efternavn Frege >>> p2 = Person("Gottlob") >>> p2.fornavn Gottlob >>> p2.efternavn Balubbenberg Bemærk valgfriheden i antallet af parametre! 12
Overstyring En operator kaldes overstyret, dersom en (eller flere) af dens parametre tillader i forskellige kald at være af forskellige typer. >>> 2+2 4 >>> 2.0+2.5 4.5 >>> "Theophilus" + " " + "Balubbenberg" Theophilus Balubbenberg Dette kan udnyttes inden for klasser og objekter, idet vi f.eks. kan tillade indbyggede funktioner at have ny semantik, når de anvendes på instanser af klasser. Eksempel (moderat fjollet): class Person: def init (self,fornavn="theophilus",efternavn="balubbenberg"): self.fornavn = fornavn self.efternavn = efternavn def add (self,other): return Person(self.fornavn + " " + other.fornavn,self.efternavn + " " + other.efternavn) >>> p1 = Person("Theophilus","Balubbenberg") >>> p2 = Person("Jeronimus","III") >>> p3 = p1 + p2 >>> p3.fornavn Theophilus Jeronimus >>> p3.efternavn Balubbenberg III 13
Polymorfi En operator er, set i Python-perspektiv blot en funktion, som kaldes pudsigt (+ f.eks. kaldes som en såkaldt infix-funktion). Altså må også funktioner kunne overstyres sådanne funktioner kaldes polymorfe. Lærebogen giver et forbilledligt godt eksempel (som vi ændrer en smule): def frontandback(front): import copy back = copy.copy(front) back.reverse() return front + back >>> liste = [1,2,3,4] >>> s = frontandback(liste) >>> print s [1, 2, 3, 4, 4, 3, 2, 1] Udvid nu klassen Person med en ny metode: def reverse(self): self.fornavn, self.efternavn = self.efternavn, self.fornavn Hver operation i kroppen af frontandback ovenfor kan anvendes på lister hhv. på instanser af klassen Person. Vi kan derfor kalde funktionen frontandback på begge dele. >>> p2 = frontandback(p) >>> p2.fornavn Theophilus Balubbenberg >>> p2.efternavn Balubbenberg Theophilus 14
Tak for i dag Husk at se på opgaverne til på fredag! 15