Ohjelmoinnin peruskurssien laaja oppimäärä

Samankaltaiset tiedostot
Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Pythonin Kertaus. Cse-a1130. Tietotekniikka Sovelluksissa. Versio 0.01b

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Poikkeukset ja tietovirrat: Virhetilanteiden ja syötevirtojen käsittely

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

Ohjelmoinnin perusteet Y Python

815338A Ohjelmointikielten periaatteet

Ohjelmoinnin peruskurssi Y1

14. Poikkeukset 14.1

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

14. Poikkeukset 14.1

Ohjelmoinnin peruskurssien laaja oppimäärä

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

Ohjelmoinnin peruskurssi Y1

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

11/20: Konepelti auki

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 6: Python

Luento 5. Timo Savola. 28. huhtikuuta 2006

Ohjelmoinnin perusteet Y Python

12. Näppäimistöltä lukeminen 12.1

Tutoriaaliläsnäoloista

Ohjelmoinnin perusteet Y Python

Poikkeustenkäsittely

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin perusteet Y Python

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Harjoitustyö: virtuaalikone

Ohjelmoinnin perusteet Y Python

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Sisältö. Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 6.2

Sisältö. Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 6.2

Ohjelmoinnin peruskurssi Y1

Sisältö Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 26.2

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 5: Python

8. Näppäimistöltä lukeminen 8.1

5/20: Algoritmirakenteita III

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Java-kielen perusteita

Tietojen syöttäminen ohjelmalle. Tietojen syöttäminen ohjelmalle Scanner-luokan avulla

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin jatkokurssi, kurssikoe

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1

Lyhyt kertaus osoittimista

1. Omat operaatiot 1.1

12/20: Bitit talteen

Sisältö. 22. Taulukot. Yleistä. Yleistä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet, syksy 2006

Java-kielen perusteet

ITKP102 Ohjelmointi 1 (6 op)

8. Näppäimistöltä lukeminen 8.1

Ohjelmoinnin peruskurssi Y1

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1

Ohjelman virheet ja poikkeusten käsittely

Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille:

Olio-ohjelmointi Virhetilanteiden käsittely

Ohjelmoinnin perusteet Y Python

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin perusteet Y Python

Muistutus aikatauluista

Sisältö. 2. Taulukot. Yleistä. Yleistä

Ohjelmoinnin perusteet Y Python

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1

Zeon PDF Driver Trial

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssi Y1

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Java-kielen perusteet

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Transkriptio:

Ohjelmoinnin peruskurssien laaja oppimäärä Luento 3: Poikkeukset, tiedostot ja tietovirrat Riku Saikkonen (osa kalvoista on suoraan ei-laajan kurssin luennoista) 8. 2. 2012

Sisältö 1 Poikkeukset 2 Poikkeusten käsittely 3 Tietovirrat

(ei-laajan kurssin kalvo: luento 4 sivu 2) Poikkeukset Ohjelmassa on lähes aina lukuisia kohtia, joissa yritetty toimenpide voi tavalla tai toisella epäonnistua. Hyvän ohjelman tulisi käyttäytyä jollain tapaa mielekkäästi silloinkin, kun jokin menee pieleen Ohjelma ei saisi kaatua kuin äärimmäisessä hädässä Esimerkkejä poikkeukseen johtavista toimista: Yritetään jakaa nollalla Tiedostoa, jota yritetään lukea, ei ole olemassa tai siihen ei ole lukuoikeuksia Yritetään hakea listasta indeksillä, jota ei ole Yritetään muuntaa merkkijono luvuksi, vaikka se ei ole lukuarvoksi kelpaava ('kissa') Tietokoneen muisti loppuu kesken Joku ohjelman luokkatiedostoista on tuhoutunut tai siihen ei pääse 11:36 käsiksi

(ei-laajan kurssin kalvo: luento 4 sivu 3) Mihin poikkeuksia käytetään? Usein poikkeustilanteeseen ei voida reagoida mielekkäästi juuri siellä (siinä metodissa), missä poikkeustilanne syntyy. Esimerkiksi mitä int()-funktion pitäisi tehdä, jos sen parametri on kelvoton? Metodin yleishyödyllisyys laskisi ratkaisevasti, jos se itse päättäisi. Metodin palautusarvoa käytetään joskus erikoistilanteiden kuvaamiseen mutta sillä on rajoituksensa. Metodi palauttaa listan pienimmän arvon. Entä jos lista on tyhjä? Poikkeusten käsittely Pythonissa perustuu siihen, että on mahdollista nostaa poikkeus (raise an exception), eli välittää poikkeustilanneilmoitus eteenpäin sellaisen ohjelmakohdan huoleksi, joka määrittelee, miten poikkeustilanne selvitetään Ellei metodi voisi nostaa poikkeusta, jouduttaisiin erikoistilanteisiin aina reagoimaan joko sen metodin sisällä, jossa ne syntyvät, tai välittämään tieto tilanteesta eteenpäin jollain vähemmän luonnollisella tavalla (paluuarvona). 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 6) Poikkeusten nostaminen (raise) Poikkeuksen luomiseen on Pythonissa erikseen määritelty raise-käsky raise-lauseelle annetaan nostettavaksi poikkeusolio raise-lause keskeyttää ohjelman normaalin suorituksen. Suoritusta jatketaan ensimmäisestä löydetystä ohjelmakohdasta, johon on kirjattu menettely kyseisenlaisten poikkeusten käsittelyyn. if homma_ei_toiminut: Tässä luodaan poikkeusolio ja nostetaan raise SomeError('Juttu X meni pieleen') se saman tien. Nämä kaksi asiaa tehdäänkin hyvin usein yhdessä. # Näille riveille ei mennä, jos poikkeus nostettiin. # Sen sijaan hypätään sellaiseen ohjelmakohtaan, # johon on merkitty erityisiä # poikkeuksenkäsittelykäskyjä. Ei onnistunut! Sä annoit tän tehtävän, joten tiedät kai sitten mitä tälle tehdään! Poikkeuksenkäsittelijärakennetta katsotaan tarkemmin kohta. Tavallisesti tällaista poikkeuksenkäsittelijärakennetta ei löydy samasta metodista raise-lauseen kanssa. Tällöin poikkeus nostetaan ylöspäin, eli ohjelman suoritus keskeytyy kokonaan ja poikkeukselle lähdetään etsimään käsittelijää kutsuvasta metodista. 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 7) Poikkeusten eri lajit Luokasta Exception on tarkoitus periä erilaisia poikkeustapauksia kuvaavia luokkia. Tämä mahdollistaa eri poikkeusten erottemisen, jos jostain toiminnasta voisi syntyä erilaisia poikkeustilanteita. Lisäksi poikkeusoliot voivat erota muutenkin kuin nimeltään. Monet moduulit määrittelevät omia poikkeuksia, jotka raportoivat moduulin funktioissa sattuneista virheistä. Kun tarkoitukseen räätälöityä poikkeusluokkaa ei ole valmiina, on sellainen helppo periyttää Exception-luokasta, tai jostain muusta. Sellainenkin Exception-luokan aliluokka, joka ei määrittele mitään uusia toimintoja voi hyvin olla perusteltu. Pelkästään tieto siitä, että kyseessä on Exceptionin alityyppi, jota on tarkoitus käyttää tietynlaisissa poikkeustilanteissa, on hyödynnettävissä. 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 8) Oma poikkeustyyppi Pyritään laatimaan luokalle Opiskelija alustusmetodi, jolle voi antaa parametrina merkkijonon muotoa Teemu Teekkari 12345 ja joka alustaa opiskelijaolion kentät tästä merkkijonosta saamiensa tietojen perusteella. (Tämä esimerkki on hieman keinotekoinen. mutta voisi kenties olla perusteltavissa esim. jos tiedettäisiin opiskelijaolioita haluttavan luoda sellaisten tekstitiedostojen perusteella, joissa opiskelijoiden tietoja olisi tallennettu riveittäin em. tavalla esitettyinä.) opiskelija = Opiskelija('Teemu Teekkari 12345') print opiskelija.kerro_opiskelijanumero() 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 9) init metodi ja poikkeus Mitä jos alla esitetty alustusmetodi saakin parametrinaan vaikkapa kissa tai vain Teemu Teekkari? Alustusmetodilla ei edes ole palautusarvoa, jota käyttää viestintään ja tavallisen metodinkin tapauksessa parempi ratkaisu voisi olla poikkeusolioiden käyttö class Opiskelija(object): def init (self, data): osat = data.split() self.etunimi = osat[0] self.sukunimi = osat[1] self.opiskelijanumero = osat[2] def kerro_opiskelijanumero(self): return self.opiskelijanumero Merkkijonolle on split-metodi, joka jakaa merkkijonon osiin käyttäen annettua parametria erottimena (oletus=tyhjä) 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 10) Oma poikkeustyyppi Kehitetään edelliselle esimerkille oma poikkeustyyppi Peritään luokasta Exception class VirheellinenOpiskelijadata(Exception): def init (self, kuvausviesti, virhedata): super(virheellinenopiskelijadata, self). init (kuvausviesti) self.virhedata = virhedata def kerro_virheellinen_data(self): return self.virhedata Tallennetaan kaikkiin tämäntyyppisiin poikkeuksiin Kuvausviestin lisäksi tieto siitä, mikä virheen aiheuttanut datamerkkijono oli. 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 11) Nostetaan poikkeus Katsotaan aluksi, vaikuttaako data oikealta. Ellei, nostetaan poikkeus. def init (self, data): osat = data.split() if len(osat)!= 3: viesti = 'Opiskelijadatassa ei ollut tasan kolmea osaa.' raise VirheellinenOpiskelijadata(viesti, data) self.etunimi = osat[0] self.sukunimi = osat[1] self.opiskelijanumero = osat[2] Nämä sijoitukset voidaan suorittaa turvallisesti tietäen, että dataa riittää. (Mikään ei kuitenkaan takaa, että annettu data on järkevää) Koska samaan metodiin ei ole merkitty rakennetta, joka käsittelisi poikkeuksen, tulee poikkeus nostetuksi ulos kutsujametodille ja tämän metodin suoritus päättyy raise-lauseeseen. (Uutta opiskelija-oliota ei synny) 11:36

Sisältö 1 Poikkeukset 2 Poikkeusten käsittely 3 Tietovirrat

(ei-laajan kurssin kalvo: luento 4 sivu 12) Poikkeuksiin reagoiminen Jos jokin metodi kutsuu toista metodia, joka voi nostaa poikkeuksen, on kutsuvan metodin koodiin erikseen merkittävä, miten se suhtautuu tähän mahdollisuuteen. Vaihtoehdot ovat: käsittely: reagoin suorittamalla nämä ja nämä koodirivit eteenpäin syöttäminen: reagoin välittämällä homman muiden ohjelman osien huoleksi. No, okei, tää on mun homma. Python ei pakota poikkeusten käsittelyyn, joten pitää olla tarkkana Vrt. palautusarvojen käyttö erikoistilanteista tiedottamiseen. En minäkään tiedä mitä sille tehdään! Hoida sinä siellä! Huomattava osa käytössä olevien ohjelmien bugeista johtuu siitä, ettei erilaisiin erikoistilanteisiin ole varauduttu huolella. 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 13) Poikkeusten käsittely Metodi, joka kutsuu poikkeuksen mahdollisesti nostavaa metodia, voi ilmoittaa itse hoitelevansa eli sieppaavansa (engl. catch) syntyvät poikkeukset (tai ainakin tietyntyyppiset poikkeukset). Tämä toteutetaan try-except-lauseella: yritän tehdä nämä hommat, mutta jos niitä suorittaessa jokin menee pieleen, niin sieppaan poikkeusolion ja katson mitä sillä teen. try: Koodia, jonka ainakin yhdestä kohdasta kutsutaan poikkeuksen mahdollisesti nostavaa metodia. except jonkintyyppinen poikkeus: Koodia, jonka suorittamalla reagoidaan tällaiseen poikkeukseen. except toisenlainen poikkeus: Koodia, jonka suorittamalla reagoidaan tähän toisenlaiseen poikkeukseen. Jne. Jos jostain try-lohkossa kutsutusta metodista nostetaan poikkeus, hypätään loppuosa lohkosta ohi ja siirrytään except-lohkoon. Jos try-lohkon suoritus onnistuu mutkitta, ei mitään except-lohkoa suoriteta. catch-lohkoja voi olla usealle eri poikkeustyypeille. Usein yksikin riittää. Kun jonkin lohkoista loppu saavutetaan, jatkuu ohjelman suoritus tavalliseen tapaan try-except-lauseen jälkeisiin lauseisiin. 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 14) Try-except esimerkki Yritetään luoda uusi opiskelijaolio ja tulostaa sen opiskelijanumero Opiskelijaluokan init voi nostaa poikkeuksen. Varautumiseen on syytä. Sijoitetaan lauseet trylohkoon. Jos init nostaa poikkeuksen, ei tulostuslausetta suoriteta, vaan hypätään exceptosioon. Tämä lohko sieppaa luokan VirheellinenOpiskelijadata olioita (mahdollisten aliluokkien ilmentymät mukaan lukien). Kun lohko aktivoituu ja sieppaa poikkeuksen, sijoittuu nostettu olio automaagisesti paikalliseen muuttujaan (tässä nimeltä dataongelma), jonka käyttöalueena on except-lohko. Vrt. metodien parametrit. try: opiskelija = Opiskelija('Teemu Teekkari12345') print opiskelija.kerro_opiskelijanumero() except VirheellinenOpiskelijadata as dataongelma: opiskelija = None print dataongelma.message print 'Virheellinen datarivi oli ' \ + dataongelma.kerro_virheellinen_data()\ + '.' Kuvausviesti saadaan Exception- luokasta perityllä messagemuuttujalla. Opiskelijadatassa ei ollut tasan kolmea sanaa. Virheellinen datarivi oli Teemu Teekkari12345. 11:36

Toinen tryexcept -esimerkki Esimerkkikoodi ja sen tulostus except.py def f(x): if x == 0: raise Exception('foo') return x + 10 z = 2 try: y = f(1) x = f(0) except Exception as e: print e z = 3 pass # foo print "y = " + repr(y) # y = 11 print "z = " + repr(z) # z = 3 print "e = " + repr(e) # e = Exception('foo',) print "x = " + repr(x) # virhe

(ei-laajan kurssin kalvo: luento 4 sivu 15) print_exc -metodi Luokan traceback metodi print_exc tulostaa luettelon, josta käy ilmi, minkä metodien kutsujen seurauksena poikkeustilanne on syntynyt. Tulostuu poikkeusta luotaessa tallennettu kutsupino (engl. call stack). print_exc ja message-metodia sekä muita kyseiselle poikkeustyypille määriteltyjä metodeita käyttäen voidaan edesauttaa virheiden etsintää ohjelmasta. Eräs poikkeuksien vahvuuksista on se, että niillä voi kätevästi välittää kuvauksia ja lisätietoja ongelmatilanteista. from traceback import print_exc try: opiskelija = Opiskelija('Teemu Teekkari12345') print opiskelija.kerro_opiskelijanumero() except VirheellinenOpiskelijadata as dataongelma: opiskelija = None print_exc() File opiskelija_trace_back.py", line 30, in <module> opiskelija = Opiskelija('Teemu Teekkari12345') File opiskelija_trace_back.py", line 20, in init raise VirheellinenOpiskelijadata(viesti, data) VirheellinenOpiskelijadata: Opiskelijadatassa ei ollut tasan kolmea osaa. 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 16) Poikkeuksen siirtäminen eteenpäin Metodi, joka kutsuu poikkeuksen mahdollisesti nostavaa metodia, voi käsittelemisen sijaan suhtautua kutsumastaan metodista nostettuihin poikkeuksiin passiivisemminkin ja siirtää ne (tai ainakin tietyntyyppiset niistä) eteenpäin sitä itseään kutsuneelle metodille. Tämä edellinen kutsuja voi sitten puolestaan jälleen joko käsitellä poikkeuksen tai siirtää sen yhä aikaisemmalle tasolle kutsujen sarjassa. Eteenpäin siirtäminen tapahtuu automaattisesti, jos poikkeus jätetään käsittelemättä. Funktio main vain siirtää VirheellinenOpiskelijadata-poikkeuksia, eikä itse määrittele niille käsittelytapaa. def main(): opiskelija = Opiskelija('Teemu Teekkari12345') print opiskelija.kerro_opiskelijanumero() Jos Opiskelijan init nostaa poikkeuksen, keskeytyy funktion suoritus Opiskelija(...) -kutsuun samaan tapaan kuin jos siinä kohdassa olisi raise-lause. init :n luoma poikkeusolio siirretään ulos myös main-funktiosta. 11:36

(ei-laajan kurssin kalvo: luento 4 sivu 18) Poikkeuksen siirtäminen ulos ohjelmasta Poikkeuksen siirtäminen ulos metodista on ongelmanratkaisun siirtämistä tuonnemmaksi. Jossakin on lopulta ilmoitettava, miten pulmaan reagoidaan tai ohjelman suoritus ei voi jatkua. Poikkeuksen voi periaatteessa siirtää aina ohjelman käynnistyskäskyyn saakka. Myös käynnistysmoduuli voi siirtää poikkeuksen, mutta tällöin vastaanottajaksi jää loppujen lopuksi enää käynnistysmoduulia tulkkiohjelman välityksellä kutsunut ohjelman käyttäjä, jonka syliin ongelma kippautuu. Tämä johtaa normaalisti ohjelman (säikeen) kaatumiseen ja virtuaalikoneen tulostamaan poikkeustilanneilmoitukseen. Erittäin huonoa tyyliä 11:36

Useita except-lohkoja Kun try-lohkon perässä on useita exceptlohkoja siirtyy suoritus poikkeuksen sattuessa ensimmäiseen lohkoista, jonka tyyppiä heitetty poikkeus on. vain yksi except-lohko suoritetaan jos mahdolliset poikkeukset ovat keskenään hyvin erilaisia ei järjestyksellä ole väliä jos sen sijaan jokin poikkeustyyppi on toisen tyypin aliluokka, tulee aliluokan käsittelevä except-lohko sijoittaa ensin. Jos näin ei tee, käsittelee yliluokan tyyppinen lohko kaikki aliluokallekin kuuluvat poikkeukset (tai itse asiassa kääntäjä kieltäytyy yhteistyöstä) Tähän törmää usein tietovirtojen käsittelyssä (käytännön esimerkki maanantaina) 11:36 (ei-laajan kurssin kalvo: luento 4 sivu 19)

try:n else ja finally try:ssa voi olla exceptien lisäksi myös else- ja finally-osat else-osa suoritetaan try-osan jälkeen jos poikkeusta ei tullut finally-osa suoritetaan lopuksi kaikissa tilanteissa (myös jos try-osa esim. palaa returnilla) yleensä finally:a käytetään erilaisten resurssien vapauttamiseen (suljetaan tiedosto tai tietokantayhteys, vapautetaan lukitus, jne.) finally siis ajetaan myös jos try-osasta tulee poikkeus monissa muissa kielissä on samankaltaiset try, except ja finally, sen sijaan else on Pythonin erikoisuus except on joskus nimeltään catch ja raise:n nimi voi olla throw (esim. Javassa)

Poikkeukset kontrollirakenteena periaatteessa poikkeuksilla voisi toteuttaa muutaman muun kontrollirakenteen: break silmukasta (tryexcept silmukan ympärille) return funktiosta tai metodista (tryexcept funktion rungon ympärille) käytännössä koodin selkeyden ja tehokkuuden vuoksi ei kannata yleinen käytäntö: poikkeus kuvaa poikkeuksellista tilannetta tai virhettä, ei tavanomaista suorituksen etenemistä joskus harvoin poikkeuksia käytetään myös muuten: esimerkiksi monimutkaisesta rekursiosta voi hypätä pois poikkeuksella poikkeuskäsittelijät ovat dynaamisesti sidottuja eli raise siirtyy kutsupinossa (ei siis raise:a ympäröivässä koodissa) sisimpänä olevaan except:iin

Poikkeusten toteuttaminen miten poikkeukset oikeasti toimivat? poikkeus hyppää aina ylöspäin kutsupinossa tai ulommas funktion sisällä ei koskaan paikkaan, jonka sisällä ei tällä hetkellä olla (paitsi tietysti except-lohkon sisään) poikkeusten toteutus voisi olla: pidetään kirjaa kutsupinosta ja koodin sisäkkäisistä rakenteista (tätä toki tehdään muutenkin kuin poikkeuksia varten) poikkeus purkaa osan näistä (samoin kuin myöhemmin tehtäisiin, jos poikkeusta ei olisi tullut) ja asettaa ohjelman tilaan tiedon poikkeuksesta tryexcept tarkistaa koodinsa suorittamisen jälkeen, onko joku asettanut tiedon poikkeuksesta (jos ei, hyppää except:ien yli, suorittaa else:n ja jatkaa) kutsupinoa purkaessa (sekä normaalisti että poikkeuksessa) suoritetaan finally-lohkot; tiedon niistä voi säilyttää pinossa

Sisältö 1 Poikkeukset 2 Poikkeusten käsittely 3 Tietovirrat

(ei-laajan kurssin kalvo: luento 5 sivu 2) Perustietovirrat Tietovirrat (streams) ovat rakenteita, joita käytetään tiedonsiirtoon ohjelman ja sen ulkopuolisen maailman välillä Virran pää on usein levyllä oleva tiedosto, verkkoyhteys, näppäimistö... Tietovirtoja voidaan tietenkin käyttää myös ohjelman sisäiseen tiedonsiirtoon. Tietovirta piilottaa kaikki allaolevan kohteen erikoisominaisuudet tarjoten joukon perusominaisuuksia joilla siihen voidaan kirjoittaa ja lukea dataa. kirjoitus luku (ja tarvittaessa luettavan odottaminen) virran sulkeminen Tämä abstraktio ei ole pelkästään Pythonille ominainen, vaan löytyy monista eri ohjelmointikielistä 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 4) Perustietovirrat Pohjimmiltaan tietovirta on tavujen jono, johon voidaan kirjoittaa tavuja, ja josta nämä tavut myöhemmin voidaan lukea samassa järjestyksessä Pythonissa tietovirtoja käsittelee esimerkiksi moduuli io ja sen luokat (IOBase ja aliluokat) Moduulista löytyy myös luokat tekstivirroille: TextIOBase ja aliluokat Riipumatta tyypistä, tietovirta pitää ensin avata open(file, mode='r', buffering=-1) fi le virran nimi mode tyyppi: luettavaksi = 'r', kirjoitattavaksi = 'w' buffering puskurointi ei käytössä (0), rivipuskurointi (1), puskurin koko (n) 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 5) Tulostus write-metodien avulla voidaan tulostaa tietovirtoihin, joissa liikkuva data koostuu tavuista. Metodi write tulostaa tietovirtaan annetut tavut tai yhden bytearray olion. write(b) Kun kirjoittaminen tietovirtaan lopullisesti päättyy, täytyy tietovirta sulkea kutsumalla metodia close(), jotta varatut järjestelmäresurssit saadaan vapautettua ja tietovirran toista päätä voidaan informoida tiedonsiirron päättymisestä. close() Sulkee tulostusvirran ja vapauttaa varatut resurssit 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 6) Tulostus (flush) Pelkkä tulostaminen tietovirtaan ei ole tae siitä, että tulostettu tieto siirtyisi välittömästi tietovirtaan. Jotta tulostaminen olisi tehokkaampaa, käytetään monesti puskureita, joilla joukko pienempiä tulostusoperaatiota saadaan suoritettua harvemmin suoritettuina suurempina operaatioina. Puskuroiduissa virroissa datan eteenpäin siirtyminen täytyy erikseen käskyttää. flush() fush-metodi pakottaa tulostusvirran puskurissa olevan datan siirtämisen virtaan. Usein close() suorittaa flush()-komennon ennen virran sulkemista. 14:58

Mitä ush tekee? kirjoituksen ush-operaatio tyhjentää lähimmän puskurin, mutta ei välttämättä vie dataa perille asti ei jää odottamaan kuittausta, että data meni perille eikä yleensä edes yritä viedä dataa kuin käyttöjärjestelmän levy- tai verkkoliikennepuskureihin asti tiedostoon kirjoitettaessa ush yleensä takaa, että muut sen jälkeen levyä lukevat ohjelmat näkevät datan levylle asti kirjoittamiseen käyttöjärjestelmät tarjoavat lisäksi operaation sync (rajapinta ohjelmointikielissä vaihtelee; Pythonissa mm. os.fsync) se yrittää tyhjentää käyttöjärjestelmän puskurit ja odottaa levyä (synchronous write eli kirjoitus tahdissa levyn kanssa) mutta valitettavasti käytännössä sekään ei aina odota lopullista kuittausta (esim. verkkolevyltä tai kovalevyn sisäisen kirjoituspuskurin tyhjentämistä) lisätietoja esim. http://www.postgresql.org/docs/current/ static/wal-reliability.html

(ei-laajan kurssin kalvo: luento 5 sivu 7) Luku Lukuvirta on tulostusvirran vastine, johon liittyviä metodeja käytetään haluttaessa lukea tavumuotoista dataa tietovirrasta Kuten vastaava kirjoitusmetodi, on lukuvirrasta mahdollista lukea yksittäinen tavu tai joukko tavuja read(n) Lukee ja palauttaa enintään n tavua Jos n puuttuu tai negatiivinen, lukee kaikki 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 9) Blocking Mitä tapahtuu kun virrasta yritetään lukea useampi merkki kuin mitä on tarjolla? Jos tietovirrassa ei ole lainkaan luettavaa dataa, ohjelman suoritus jää odottamaan että dataa tulee tarjolle. Tätä odotusta kutsutaan nimellä blocking Read-metodit lukevat tietovirrasta useamman kerran, jotta haluttu määrä tavuja täyttyisi 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 11) Esimerkki def read_from_stream(stream): num_read_bytes = 1 buffer = bytearray() # Yritetään lukea maksimissaan 1024 merkkiä kerrallaan, # jos virta on suljettu, lukuoperaatio palauttaa -1 # muutoin saamme tavujonon, jonka pituus on välillä 1-1024. # Pituus kertoo montako merkkiä # lukuoperaatio tällä kertaa ehti lukea kunnes puskuri tyhjeni. # # silmukassa siirretään kirjoituskursoria eteenpäin luetun datan verran. while num_read_bytes!= 0: read_bytes = stream.read(1024) num_read_bytes = len(read_bytes) if num_read_bytes!= -1: buffer.extend(read_bytes) # lopuksi varmistetaan että virta suljettiin stream.close() return buffer 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 12) Merkkijonovirrat Merkkijonovirrat eroavat tavuvirroista siinä, että niiden data koostuu tavujen sijaan merkeistä (char) TextIOBase ja sen johdannaiset ovat käteviä tekstimuotoisen datan käsittelyyn TextIOWrapper on tavuvirran päällä oleva puskuroitu tekstivirta Luokka StringIO on muistinsisäinen Unicode-tekstivirta. 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 13) Puskuroidut virrat Luokkien BufferedReader/Writer ja TextIOWrapper tehtävä on vähentää allaolevan tietovirran tapahtumia. Jos näihin virtoihin kohdistetaan pieniä kirjoitusoperaatioita, kerätään kirjoitettua dataa puskuriin josta se siirtyy allaolevaan virtaan vasta puskurin täyttyessä. Kuvittele esim tilanne jossa kirjoitetaan kovalevyllä olevaan tiedostoon dataa merkki kerrallaan. On luonnollisesti tehokkaampaa suorittaa kirjoitus suuremmissa erissä. Halutessaan käyttäjä voi pakottaa puskurin tyhjennyksen kutsumalla metodia fush() Vastaavasti syötevirrat pyrkivät lukemaan kerralla suurempia lohkoja dataa sisään puskuriin, johon kohdistetut pienetkään operaatiot ei aiheuta suurta määrää tapahtumia allaolevassa virrassa. 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 14) IOError Yksi syy miksi tietovirtojen lukemista ei opeteta kurssin alkupuolella on se, että melkein kaikki tietovirtoihin liittyvät metodit saattavat nostaa poikkeuksen IOError Tämän vuoksi lähes kaikki tietovirtojen kanssa tehtävä toiminta suoritetaan try-except lohkon sisällä. 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 15) Esimerkki, näppäimistön luku import sys def main(): rivi_lukija = sys.stdin try: luettu_rivi = rivi_lukija.readline().strip() while luettu_rivi and luettu_rivi!= 'exit': print luettu_rivi.upper() luettu_rivi = rivi_lukija.readline().strip() except IOError: print u'lukeminen p\u00e4\u00e4ttyi virheeseen' 14:58

(ei-laajan kurssin kalvo: luento 5 sivu 17) Tiedoston luku try: tiedosto = open("esimerkki.txt", 'r') except IOError: print "Tiedostoa ei ole." exit() try: luettu_rivi = tiedosto.readline() while luettu_rivi: print luettu_rivi luettu_rivi = tiedosto.readline() except IOError: print u'lukeminen p\u00e4\u00e4ttyi virheeseen' tiedosto.close() 14:58

Automaattinen sulkeminen: with Kaksi tapaa lukea tiedostoa readfoo.py def readfoo(): s = 0 f = open('foo.txt', 'r') # nämä kaksi riviä try: # tai: with open('foo.txt', 'r') as f: for line in f: if line.startswith('end'): return s cols = line.split() s += int(cols[0]) * int(cols[1]) finally: # f.close() # ja nämä rivit pois return -1 Pythonin with on lyhennysmerkintä tietynlaiselle tryfinally:lle tiedostoille (open) se kutsuu close:a automaattisesti (vaikka with:n sisällä olisi tullut poikkeus) vrt. Schemen with-input-from-file with ei ole vain tiedostoille, myös mm. itse tehdyille luokille toteutus kutsuu with:lle annetun olion enter -metodia alussa ja exit -metodia lopussa