HØit Nr. 1-99
Previous article Next article TOC: Nr. 1, 1999 Previous Issue Next Issue About HØit

En introduksjon til programmeringspråket Python


Harald Nordgård-Hansen

Sammendrag

I denne artikkelen presenteres programmeringspråket Python, et moderne scriptspråk. Python gir mulighet for å benytte avanserte abstraksjoner til hurtig å utvikle program. Videre er koden direkte kjørbar på forskjellige plattformer, også inkludert grafiske grensesnitt osv. For dem som ønsker det, er koden kompilerbar, både til python byte-kode, og til enkeltstående kjørbar fil.

Viktigst er kanskje likevel at Python er utviklet med tanke på at det skal være enkelt å lære seg språket, og også å bruke om igjen kode skrevet i Python i nye prosjekt. Både syntaks og innhold i språket er laget for å hjelpe brukeren til å forstå hva et program gjør enklest mulig.

Om verktøy for hurtig programutvikling (RAD)

I moderne programutvikling søker vi stadig etter nye måter å la datamaskinen gjøre mest mulig av arbeidet for oss når vi lager program. Dette varierer fra at vi bruker enkle bibliotek med ferdige rutiner, og opp til automatisk programgenerering[1]. På den kommersielle siden dominerer for øyeblikket grafiske grensesnitt-byggere, hvor man tegner grafikken som programmet skal lage, og utviklingsomgivelsene automatisk lager koden som skal brukes. Sammen med fornuftige bibliotek av ferdige funksjoner har dette bidratt til at det å lage interaktive program nå er blitt en ganske effektiv prosess.

Men likevel er det mye igjen å hente. Oftest bruker vi nå kompilerte språk som C/C++ og Java. Selv om dette gjerne lager relativt rask kjørbar kode, er det forbundet med veldig mye ventetid under utviklingen av programmene. Fra vi er ferdig med å gjøre (små) endringer et sted i koden vår, og til kompileringen og lenkingen av programmet er klart, tar det alltid en viss tid. Og selv om nyere maskiner har tilstrekkelig regnekraft til at man ikke lenger rekker å ta en kopp kaffe, så har det oftest tatt tilstrekkelig tid til at tankerekken vi var i da endringene i koden ble utført er brutt.

I stedet for (eller i tillegg til) å tilby grafiske grensesnitt-byggere, er likevel det jeg oftest savner i våre nåværende verktøy støtten for mer avanserte datastrukturer enn det vi er vant til. For matematikk er det ikke tilfeldig at et språk som matlab, med sin innebyggede støtte for vektor- og matrise-variable, er mye mer effektivt enn tradisjonelle programmeringspråk. (Det er heller ikke tilfeldig at nyere Fortran-dialekter er utvidet i samme retning.) Men for vanlige oppgaver i generelle problemer, blir det oftest til at støtten i et programmeringspråk blir for svak og vi er henvist til å kjøpe ferdige bibliotek, eller vi lager våre egne løsninger i stedet. I begge tilfeller har vi likevel en løsning som er lagt til etterpå, og som ikke alltid integrerer like godt i språket.

Om å lære å programmere

Et annet emne jeg har lyst til å trekke frem er valg av programmeringspråk for opplæring. Vi er tross alt en utdanningsinstitusjon, og en viktig del av det vi holder på med, er å lære studentene å lage program. Når man så har undersøkt hva som skjer når studentene første gang lærer å programmere[2], så finner man at det aller meste av innsatsen går med til å lære seg syntaksen i det bestemte språket, mens selve omformingen av et problem til en form som lar seg automatisere forsvinner i et virvar av spesialtegn, kompilatorfeil og frustrasjon.

Utfordringen vår blir altså å få frem denne forståelsen i våre studenter; at man trenger å formulere problemet sitt innenfor en programmerings-strategi for så å legge en syntaks på den. Så kan man velge det av de språkene man behersker som passer best til den aktuelle oppgaven.

Men i stedet for at man lærer seg en slik angrepsvinkel, som faller ganske naturlig for dem som har programmert i mange språk over tid, så viser det seg at når man skal lære seg sitt første programmeringspråk, blir det i praksis utelukkende syntaks man tenker på, i alle fall så lenge det er tilstrekkelig kompleks syntaks i det språket man bruker.

Om gjenbruk av kode

Gjenbruk av kode har vært et viktig forskningsfelt nesten så lenge som det har blitt programmert. Men likevel ser vi at man fremdeles ofte ender opp med å skrive omtrent samme kodebit hver gang man har bruk for den.

Dette skyldes delvis at man ikke er vant til å skrive tilstrekkelig generelle løsninger når man lager sin kode, men oftere skyldes det rett og slett at det er enklere å skrive koden på nytt enn å skulle sette seg inn i den eksisterende koden godt nok til å kunne tilpasse den.

For å lage kode som er mulig å bruke om igjen, er det viktigste altså å lage kode som er lett å forstå. En del kan avhjelpes hvis man passer på ikke å bli fanget av "Oualline’s law of documentation"[3]:

90% of the time the documentation is lost. Out of the remaining 10%, 9% of the time the revision of the documentation is different from the revision of the program and therefore completely useless. The 1% of the time you actually have documentation and the correct revision of the documentation, it will be written in Japanese.
For å unngå dette problemet er Ouallines oppskrift ganske enkel: Legg den dokumentasjonen som trengs inn i programmet. Og sørg for å skrive selve programkoden slik at den i størst mulig grad dokumenterer seg selv og man kan forstå hva som skjer uten kommentarer. Også slike virkemidler som konsistent indentering og oppdeling av koden i funksjonelle enheter er viktig for å lage kode som er lesbar både for andre og ikke minst for en selv 2 måneder senere.

Python

Python ble først laget i juleferien 1989 av nederlenderen Guido van Rossum, og har arvet mange av idéene sine fra et undervisningspråk han hadde arbeidet med som het ABC, samt fra Modula-3. Det betyr at Python er et relativt "rent" språk, det er laget for at koden man skriver skal være så lett som mulig for andre å forstå, og det er et av de lettere språkene å lære som finnes i dag.

Likevel er Python et komplett, objektorientert programmeringspråk, og har god støtte for hurtig utvikling av leselig og gjenbrukbar kode. Eller for å sitere fra sammendraget til Python Tutorial[4]:

Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python’s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms.

The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python web site, http://www.python.org, and can be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation.

The Python interpreter is easily extended with new functions and data types imple-mented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications.

Det var salgsfremstøtet, la oss så se litt på selve språket:

Noen enkle programeksempler

Python er altså et språk som benytter dynamisk typing av variable. Det betyr at variable ikke deklareres, men lages første gang de benyttes. Så konverteres typen til variabelen til det man trenger etter hvert som den brukes. Python har i motsetning til de fleste andre dynamisk typede språk automatisk skjuling av variable deklarert inne i blokker, så man er nødt til å spesifisere globale variable som globale for å overstyre normal oppførsel.

For å kjøre funksjoner og innebyggede kommandoer, kalles disse som man er vant til i fra andre språk. Men for en del innebyggede kommandoer er parentesene rundt argumentlisten valgfri (typiske eksempler er if og print). Hvis man skal bruke tekst-strenger settes disse enten inne i " " eller ' '. Det skulle være nok til å presentere "Hello, World"-programmet:

print "Hello, World"
Det er ikke noen spesielle tegn som brukes til å avslutte en kommando, en kommando avsluttes av linjeskift. Det er heller ikke noen andre linjer som trenger å være med for å lage et komplett program.

Print-kommandoen skriver ut sine argument, og avslutter med linjeskift. Hvis man gir flere argumenter med komma mellom, skrives hvert argument ut, adskilt med mellomrom. Og hvis argumentlisten til print avsluttes med komma, så vil print skrive ut et mellomrom i stedet for avsluttende linjeskift, slik at flere print-kommandoer kan skrive ut til samme linje:

print "Hello,", 
print "World"
Noe av det mer spesielle i syntaksen til Python, er at det ikke finnes noen tegn som lager en blokk. I stedet benyttes indentering til å angi hva som hører sammen. Så en enkel for-løkke vil bli:
for i in range(1,10): 
   print i,"*",i,
   j = i*i
   print "blir",j
print "Ferdig"
Det faller noen ganske tungt at man har en slik kobling mellom visuelt utseende og logisk struktur, men effekten blir at man tvinges til å lage kode som alltid er korrekt indentert, og over tid holdes koden således mye mer leselig enn det som er normalt i andre språk.

Å definere funksjoner

For å lage en funksjon i Python, bruker man def navn(arglist):. Deretter følger en valgfri dokumentasjonstekst, og så en blokk som definerer selve funksjonen. Så en funksjon som returnerer Fibonacci-tallene opp til n blir da:
def fib(n):
   "Returner en liste med Fibonacci-tallene opp til n"
   result = []
   a, b = 0, 1
   while b < n:
      result.append(b)
             a, b = b, a+b
   return result
Vi ser også her at tilordning ikke behøver å skje bare til en enkelt variabel. Det betyr blant annet at man ikke trenger å benytte noen ekstra variabel for å bytte om to variable. Man kan ganske enkelt si a,b = b,a. Python har ganske mange slike konstruksjoner som er laget for å forenkle hverdagen til en programmerer, uten at man ofrer lesbarheten til koden av den grunn.

Når man definerer funksjoner, kan man også tilordne default-verdier til argumentene. Det er da valgfritt om disse defineres når man senere kaller funksjonen. Så hvis funksjonen over i stedet hadde blitt definert som def fib(n=100), så ville man kunne kalle funksjonen uten argument, og ville da få ut tallene under 100.

Hvis man definerer en funksjon med flere argument, og mange default-verdier, er det ofte et ønske å kunne gi inn verdier for noen enkelte av argumentene. Hvis disse kommer først i definisjonen er det ikke noe problem, men for å sette argumenter lenger ut i listen trenger man en annen mekanisme. Vi tar utgangspunkt i en funksjon definert som:

def ask_ok(prompt, retries=4, \
complaint=’Yes or no, please!’): 
   ...
Her kan vi kalle denne som ask_ok("Virkelig avslutte?") eller kanskje ask_ok("OK å slette filen?",2). Men hvis vi ønsker å benytte default-verdien til retries, men overstyre complaint, kan vi ganske enkelt gjøre dette ved: ask_ok("Avslutte?", complaint="Svar ja eller nei"). Det er altså mulig å referere til enkelt-argument ved navn, selv om det da er opp til programmereren å sørge for at et argument kun tilordnes verdi én gang i et kall.

I tillegg er det selvsagt mulig å definere funksjoner med variabelt antall argumenter. Hvis man etterfølger de vanlige argumentene til funksjonen med *navn, vil alle overskytende argumenter legges inn i listen navn. Tilsvarende kan man også ta inn argument gitt ved ikke-definerte navn i en ordliste navn ved å definere argumentet **navn til funksjonen.

Datastrukturer

Vel så viktig som syntaksen er kanskje datastrukturene som finnes i Python. I fibonacci-funksjonen over har vi laget en variabel result, som inneholder en liste med tall. Denne kan man da hente ut deler av, legge til inne i, og generelt manipulere ganske fritt. Da vi laget result som en liste, ble denne altså et objekt av liste-klassen. Python er objektorientert, og listeklassen inneholder forskjellige metoder som for eksempel append(x), reverse() og sort().

Den kanskje nyttigste datatypen er nok likevel ordlisten (dictionary). Dette er den samme konstruksjonen som Perl sine assosiative arrayer, og gir i effekt en enkel database-funksjon innebygget i språket.

En ordliste er på samme måte som en liste en variabel som bruker en index. Men til forskjell fra listen hvor index er et tall i en rekke, er indexen i en ordliste en vilkårlig konstant, det være seg tekst, tall eller tupler av disse. Den vanligste bruken er nok likevel med tekst-nøkler.

For å hente ut alle nøklene som er brukt i en ordliste bruker man metoden keys() som returnerer en usortert liste. Man kan også teste om en nøkkel er definert ved metoden has_key().

Denne innebyggede støtten gjør at man veldig effektivt kan behandle strukturert informasjon. Som et eksempel kan brukes registreringen av et nytt innslag i databasen for leverte øvinger i web-kurset. Her brukes gdbm-biblioteket, som lager enkle én-nøkkels database-filer. Etter å ha verifisert gyldigheten til inn-data fra studentene, kjøres koden, hvor oving og gruppe identifiserer hva som er levert, og url er referansen til der besvarelsen fra studentene ligger:

db = gdbm.open(dbfile,"w") 
db[oving + "-" + gruppe] = url
db.close()
For å hente ut eller endre det som ligger i databasen, kan man helt enkelt referere til eller tilordne verdien av db["min_nøkkel"], og hvis en har bruk for å slette en nøkkel brukes del db["min_nøkkel"].

Forskjellige programmerings-paradigmer

Så langt har beskrivelsen stort sett omfattet hvordan man programmerer tradisjonell imperativ kode. I stor grad brukes likevel Python til objekt-orientert programmering. Objektorientert programmering i Python ligger et sted mellom det som tilbys i C++ og det som tilbys i Modula-3. Ved hjelp av et minimum av nye nøkkelord tilbyr Python multippel arving, redefinering av arvede funksjoner og innebyggede operatorer osv. Man har også støtte for å lage dynamisk lastede moduler med kode, og alle klasser er selv objekter. Men selv om man har støtte for private medlemmer i en funksjon, baserer det seg på at brukeren av klassen oppfører seg ordentlig og ikke forsøker å "bryte" seg inn.

For dem som bedre liker funksjonell programmering, har Python også støtte for lambda til å lage små anonyme funksjoner. Disse kan så sendes i variable, på samme måte som vanlige funksjoner. Ved hjelp av de innebyggede funksjonene filter, map og reduce har man det sentrale man trenger for å programmere i en funksjonell modell i Python.

Når det gjelder deklarativ programmering, er nok Python enda ikke noe alternativ til Prolog. Man kan ikke få alt her i verden. På den annen side har man støtte for exceptions for feilhåndtering, og bibliotekene for event-basert programmering av grafiske grensesnitt er til-gjengelig.

Plattformuavhengig programmering

Siden Python i utgangspunktet er et script-språk, og man kjører "kildekoden" direkte, så er koden man skriver helt uavhengig av plattform. Python finnes også tilgjengelig for de fleste Unix-varianter, Win16 og 32, dos, Mac, OS/2, QNX, osv. I tillegg er for eksempel tilgangen til operativsystemet pakket inn i en plattformuavhengig modul, som oversetter koden til det som tilbys lokalt, og emulerer viktige ting som eventuelt ikke finnes.

Hvis man ønsker å lage grafiske program ved hjelp av Python, finnes også diverse bibliotek for dette. Noen av disse er knyttet til spesielle plattformer, men TkInter-pakken, som er det ofisielle grafiske biblioteket til Python (og basert på Tk/Tcl) er tilgjengelig for de forskjellige plattformene, og kjører da uendret.

Når man så er ferdig med å utvikle et program, har Python blant annet støtte for å kompilere koden til Python byte-kode. Koden vil da starte opp raskere enn ellers, men man trenger fremdeles Python installert for å bruke programmet. I tillegg finnes et hjelpeprogram som følger med kildekoden til Python. Hvis man har tilgjengelig verktøyene til å bygge Python, kan man ved hjelp av dette verktøyet konvertere et Python-program til en kjørbar kompilert fil. Denne trenger da ikke noen andre filer, og lager en plattformavhengig applikasjon man kan distribuere.

Oppsummering

Når vi velger våre verktøy har vi en tendens til enten å holde oss til det vi allerede føler at vi behersker, eller å velge det som den kulørte datapresse i dag forteller oss at er veien til frelse. Men jeg tror vi gjør både oss selv og studentene våre en stor bjørnetjeneste ved ikke å sette oss inn i det som finnes av verktøy rundt om kring i verden.

Python er et eksempel på en programmeringsomgivelse som har i seg mange av de pedagogiske egenskapene til for eksempel Logo, men som er et komplett språk som uten problem kan brukes i store prosjekter. Det er ikke nødvendig for oss lenger å slite med tungvinte systemer bare for å kunne arbeide med reelle problemstillinger.

Så jeg vil oppfordre alle til å ta utfordringen med å lære seg et nytt språk i dag. Python er i så måte et godt alternativ.

Referanser

  1. J.R. Olsson. Låt datorn skriva programmen! HØit, 1, 1998.
  2. K. Valstadsve. Fully Functional - modern approaches to teaching engineering science. NTNU, 1996.
  3. S. Oualline. Practical C Programming. Nutshell Handbooks. O’Reilly & Associates, 1991.
  4. G. van Rossum. Python Tutorial. Rel. 1.5.1, August 1998.

Previous article Next article TOC: Nr. 1, 1999 Previous Issue Next Issue About HØit
HØit Nr. 1-99


Copyright: 1998, 1999, Høgskolen i Østfold. Last Update: March.99, Jan Høiberg.