2.20. Klasser

Objekter som flyvemaskiner, rugbrød og knapper i et Python GUI program har egenskaber som eksempelvis, der kan være fælles. Eksempler på egenskaber er farve og størrelse. Jo flere egenskaber, de har fælles, jo nærmere er de beslægtede. De kan have så mange fælles egenskaber, at det bliver naturligt at samle dem i fælles grupper eller klasser. I OOP (objektorienteret programmering - se først i bogen) går ud på at udnytte (genbruge) fælles egenskaber.

Klasser repræsenterer grupper af objekter med fælles egenskaber. At egenskaberne er fælles kan i høj grad bruges til noget, når det gælder Python og andre objektorienterede computersprog. Programmøren skal ikke genopfinde de fælles egenskaber, men kan lade sine klasser arve de egenskaber andre klasser allerede er i besiddelse af. Han eller hun fortæller Python, at der skal oprettes en klasse ved at indsætte nøgleordet class først på en programlinje eks:

class Klasse:
        pass

Det viste eksempel arver ikke direkte nogen som helst egenskaber, men der skal selvfølgelig en masse kode til for at danne de nødvendige forbindelser (interface mellem eksemplet og fortolkeren). Det kan følgende antyde:

>>> dir(Klasse)
['__doc__', '__module__']
>>>

Her ser du endnu et eksempel, på hvor beskeden en fuldgyldig Python klasse kan være:

>>> class Klasse:
        """ Dette er en fuldgyldig Python klasse."""


>>> class Klasse:
        pass

Defineringen skal indledes med class, der skal efterfølges af et lovligt navn og et kolon, hvor sidstnævnte er indledningen til klassens krop. Denne indledning er samtidig indledningen af en blok. I denne blok skal der være mindst en fejlfri programlinje. De er der i begge de viste eksempler, hvorfor de er helt OK. Det vil jeg godt se et bevis på lige nu og her:

Det kan vi få en kontrol på lige her og nu:

>>> class Klasse:
        """ Dette er en fuldgyldig Python klasse."""

>>> Klasse
<class __main__.Klasse at 0x8291e24>
>>>


>>> class Klasse:
        pass

>>> Klasse
<class __main__.Klasse at 0x827038c>
>>>

Klasse opfylder alle betingelser for at være en fuldgyldig Python klasse. Den begynder med nøgleordet class, har et navn, der er fuldt lovligt og dermed brugbart, og den omfatter en krop, der begynder med kolon og den i Python obligatoriske indrykning. Nøgleordet pass gør ingen ting, og det er netop formålet med pass.

Som vist kan det konstateres, at Klasse virkelig er en fuld færdig og lovlig Python klasse ved fra interaktiv mode at skrive klassenavnet (Klasse) og trykke på Enter-tasten.

x = Klasse()

Test:
>>> x
<__main__.Klasse instance at 0x826ca94>
>>>

I Python er der en række muligheder for at få uddybende informationer om en klasse. Dem vil vi se på her i indledningen til afsnittet om klasser:

>>> dir
<built-in function dir>

Den fordefinerede eller i Python indbyggede (built-in) funktion dir kan med
sin parameterliste fortælle, om Klasse nu også er en klasse.

>>> dir()
['Klasse', 'PyShell', '__builtins__', '__doc__', '__name__']
>>>

Der oprettes en liste, hvis første element er klassenavnet selv. Ved konstruktionen af Klasse er PyShell anvendt, hvad PyShell er, kan vi også få oplyst:

>>> PyShell
<module 'PyShell' from '/usr/lib/python2.2/site-packages/idle/PyShell.py'>
>>>

Det kan vel næppe komme som en overraskelse, at der er anvendt et modul til konstruktionen af vores Klasse, men prøv lige selv at lave følgende test, og du vil overraskes over, hvor mange stumper, der er anvendt til konstruktionen:

>>> import PyShell
>>> dir(PyShell)
['ACTIVE', 'ALL', 'ANCHOR', 'ARC', 'At', 'AtEnd', 'AtInsert', ...

Nederst i den lange udskrift, hvorfra jeg kun har medtaget starten, genfinder du

'__builtins__', '__doc__' og '__name__',

Helt tilsvarende kan man få oplysning, om hvilke programstumper (OOP dele), der indgår i Klasse:

>>> dir(Klasse)
['__doc__', '__module__']
>>>

Dem vil vi se nærmere på i forbindelse med Klasse, men Klasse er defineret tom, så vi behøver en udvidelse af erklæringen:

>>> class Klasse:
        def _init_(self):
                pass

"def _init_(Klasse)" er på sin vis 3 ting: 1: definering af funktionen _init_ og 2: initialisering (grundlæggende værditildeling til Klasse) og 3: Klasses konstruktør. Argumentet self fortæller, at Klassen bruger sine egne egenskaber, hvad det vil sige, skal vi snart se, men lad os først lave endnu en kontrol:

>>> dir(Klasse) ['__doc__', '__module__', '_init_']

Vi ser, at _init_ er indsat som element i listen.

>>> Klasse._init_ <unbound method Klasse._init_>

Vi har endnu ikke importeret et modul. Det vil vi gøre:

>>> import math >>> class Klasse(math): print math.pi

3.14159265359 >>>

Test: >>> import math >>> math <module 'math' from '/usr/lib/python2.2/lib-dynload/math.so'> >>>

Når Python-fortolkeren indleder kørslen af et program, er værdien af _name_ lig med _main_ (læg godt mærke til det, for programlinjen indgår i en lang række af bogens eksempler, hvor det er svært at kommentere og samtidig bevare læsbarheden, og den derfor udelades programlinjen er if _name_ == "_main_": eks. if __name__ == '__main__': Dialog().mainloop() det skal læses som: Hvis det er klassen Dialog, der startes op, så skal løkken mainloop startes op - den kontrollerer, om der indtræder nye hændelser (events) fra program og/eller bruger.)

Namespace og scope er to navne for det samme - at angive et navns virkningsområde eller sagt på en anden måde det område, hvori navnet kan bruges. Det kan følgende eksempel belyse:

>>> x = 256
>>> class Klasse:
        print x
        
256
>>>

Klasse har ene og alene mulighed for at kende navnet x og dermed for at kunne returnere variablens værdi, fordi v er defineret som global variabel (globalt navn). Det fører videre til:

x = 1 # global variabel

# ændrer den lokale variabel x (shadows (skygger for) den globale variabel def a(): x = 25

print "\nVærdien i den lokale x er", x, "efter defineringen og kaldet af a" x += 1 print "Den lokale x (den i a) er",x, #før a forlades"

# ænder den globale variabel x def b(): global x print "\nDen globale x er nu",x,"Ved kald fra b" x *= 10 print "Globale x er",x,"når b er forladt"

print "Globale x er",x x = 7 print "Globale x er", x

a() b() a() b()

print "\nGlobale x er" ,x

Kørselsresultat:

Globale x er 1

Globale x er 7

Værdien i den lokale x er 25 efter defineringen og kaldet af a

Den lokale x (den i a) er 26 Den globale x er nu 7 Ved kald fra b

Globale x er 70 når b er forladt

Værdien i den lokale x er 25 efter defineringen og kaldet af a

Den lokale x (den i a) er 26 Den globale x er nu 70 Ved kald fra b

Globale x er 700 når b er forladt

Globale x er 700

>>> class Klasse:
...     i = 123
...     def f(self):
...             return "Python klasser er i en klasse for sig."
...

Klasse.i og Klasse.f er begge lovlige attribut referencer, der henholdsvis returnerer et heltal og brugt på rette måde resultatet af en metodeafvikling. Læg mærke til, at for at få nævnte resultat returneret, så skal både klasse og metode have parameterliste.

>>> Klasse.i
123
>>> print Klasse().f()
Python klasser er i en klasse for sig.
Har du gemt nogle af dine definitioner i py filer, har du for så vidt allerede lavet Python moduler, der principielt fuldstændig svarer til de fordefinerede moduler i Python. Men skriv de to følgende funktioner i en editor og gem dem med navnet fibonacci.py
def fib(n):
        a, b = 0, 1
        while b < n:
                print b,
                a, b = b, a + b
def fib2(n):
        resultat = []
        a, b = 0, 1
        while b < n:
                resultat.append(b)
                a,b = b, a + b
        return resultat

fibonacci.py er et ganske normalt python modul og skal derfor også importeres på den helt normale måde. Start Python op i interaktiv mode og skriv følgende kode:

>>> import fibonacci >>> fibonacci.fib(100) >>> fibonacci.fib2(100)

Gør du det vil programafviklingen se således ud: >>> import fibonacci >>> fibonacci.fib(100) 1 1 2 3 5 8 13 21 34 55 89 >>> fibonacci.fib2(100) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>>

Hvis du har til hensigt at bruge en funktion ofte, kan du tildele den et lokalt navn: >>> import fibonacci >>> f = fibonacci.fib

Et kørselsresultat: >>> f(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 >>>

Naturligvis kan du også bruge  type og dir  funktionerne på fibonacci.py:
>>> type(fibonacci)
<type 'module'>
fibonacci.py er altså et modul, hvad vi  vel nok kunne ane.
>>> dir(fibonacci)
['__builtins__', '__doc__', '__file__', '__name__', 'fib', 'fib2']
Her returneres liste indeholdende alle navne, variabler, moduler,
funktioner m.v., der er defineret i øjeblikket.
>>> dir()
['__builtins__', '__doc__', '__name__', 'fibonacci']
>>>

Uden argumenter returnerer \dir() de navne, du har defineret i øjeblikket. Det drejer sig alene om fibonacci modulet. dir() lister ikke den indbyggede (fordefinerede) funktioners og variablers navne, hvis du ønsker at se dem, så kan det ske ved at udskrive en liste af standardmodulet \__bultin__ 's indhold:

>>> dir(__builtin__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning', ....
>>> import sys
>>> sys.path = ".:/usr/local/lib/python"

Kørselsresultat: >>> sys.path ['', '/usr/local/lib/python23.zip', '/usr/local/lib/python2.3', '/usr/local/lib/ python2.3/plat-linux2','/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/ python2.3/lib-dynload', '/usr/local/lib/python2.3/site-packages'] >>>

>>> PYTHONPATH = sys.path # PYTHONPATH skal defineres for at kunne bruges

Et kørselsresultat:
>>> PYTHONPATH
['', '/usr/local/lib/python23.zip', '/usr/local/lib/python2.3', '/usr/local/lib/
python2.3/plat-linux2', '/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/
python2.3/lib-dynload', '/usr/local/lib/python2.3/site-packages']
>>>

Som du ser, returnerer PYTHONPATH en liste. I den er de aktuelle stier, de stier hvori fortolkeren vil søge for at finde moduler m.v. Det kan du udnytte ved at tilføje det eller de biblioteker, hvori du gemmer dine selvkomponerede klasser, som element eller elementer. I det store og hele er det underordnet, hvor du placerer dine moduler. Jeg oprettede biblioteket mineModuler i mit brugerområdes rod. Nu kan jeg flytte mine moduler dertil og indsætte biblioteket som element i listen:

PYTHONPATH.append("/home/ajbo/mineModuler")

Kørselsresultat: >>> PYTHONPATH ['', '/usr/local/lib/python23.zip', '/usr/local/lib/python2.3', '/usr/local/lib/python2.3/plat-linux2', '/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/python2.3/lib-dynload', '/usr/local/lib/python2.3/site-packages', '/home/ajbo/mineModuler'] >>>

Det er også muligt at adskille sys.path og PYTHONPATH,
så de hver især kan bruges efter behov:
>>> PYTHONPATH = "/home/ajbo/mineModuler"
>>> PYTHONPATH
'/home/ajbo/mineModuler'
>>> sys.path
['', '/usr/local/lib/python23.zip', '/usr/local/lib/python2.3',
'/usr/local/lib/python2.3/plat-linux2',
'/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/python2.3/lib-dynload',
'/usr/local/lib/python2.3/site-packages']
>>>

Del sætningen kan bruges på 2 forskellige måder. Der er en måde, hvorpå et element kan fjernes fra en liste ud fra elementets indeks-nummer i stedet for selve elementet. Den kan bruges til at fjerne slices fra en liste. Og som eks. 2 viser kan del også fjerne selve listen.

Eksempel 1:
>>> liste = [-1, 1, 66.6, 333, 333, 1234.5]
>>> del liste[0]
>>> liste
[1, 66.6, 333, 333, 1234.5]

Eksempel 2: >>> del liste[2:4] >>> liste [1, 66.6, 1234.5]

Eksempel 3:
>>> del liste