Arcade-pelien ohjelmointi
Pythonilla ja PygamellaChapter 18: Poikkeukset
Jos jotain menee ohjelmassa pieleen, ei Pythonin punainen virheilmoitus ei ole kovin mieluisaa nähtävää. Mitä, jos ohjelma tilttaa, miten saat suljettua ohjelman hallitusti? Tässä kohtaa tulee käyttöä poikkeuksille.
Poikkeuksilla voidaan käsitellä kaikki epätavalliset tilanteet ohjelman suorituksen aikana. Poikkeuksia käytetään tiedosto-operaatioissa ja verkkoliikenteen virhetilanteissa. Poikkeuksilla saadaan hallitusti käsiteltyä tallennustilan käsittelyvirheet, verkkoliikenteen virheet ja käyttöoikeusvirheet.
18.1 Sanastoa
Poikkeusten käsittelyyn liittyy muutamia käsitteitä ja termejä. Tässä muutama yleisesti käytössä oleva:
- Poikkeus:: Voi tarkoittaa kahta eri asiaa. Ensinnäkin, ilmaisee jonkin epänormaalin tilanteen ohjelman suorituksessa. Toisaalta voi tarkoittaa myös objektin tilaa. Jokainen poikkeus on objekti, joka sisältää tietoa poikkeuksesta.
- Poikkeuksen käsittely: Toiminto, jolla poikkeus käsitellään normaalin ohjelman suorituksen aikana.
- Poikkeuslohko tai Catch lohko: Se koodinosa, joka 'pyydystää' poikkeuksen.
- Throw tai nosto: Kun ohjelmassa havaitaan poikkeava tilanne, luodaan poikkeuksesta olio (nostetaan olio), joka käsitellään koodissa.
- Käsittelemätön poikkeus tai Pyydystämätön poikkeus: Nostettu poikkeus, jota ei ole koskaan käsitelty. Tämän seurauksena ohjelma 'krässää' eli päättyy odottamattomasti.
- Try lohko: Koodilohko, jossa mahdollinen poikkeus(olio) on luotuna.
Useimmat ohjelmointikielet käyttävät termiä “throw” ja “catch.” Valitettavasti Pythonissa tätä ei käytetä. Python käyttää “raise” ja “exception” termejä. Käsitepari throw/catch on tässä mukana koska ne ovat käytössä yleisemmin käytössä olevissa ohjelmointikielissä.
18.2 Poikkeuksen käsittely
Poikkeuksen käsittelevä koodi on yksinkertainen. Siitä esimerkki alla:
# Divide by zero try: x = 5 / 0 except: print("Error dividing by zero")
Rivillä kaksi on try lause. Jokainen sisennetty koodirivi tässä lohkossa kuuluu “try block” lohkoon. try lohkon alla voi olla myös sisentämätöntä koodia , jota ei aloiteta except lauseella. try osassa määritetään suoritettava ohjelmakoodi.
Jos ohjelmasta nousee poikkeus suorituksen aikana, siirtyy suoritus välittömästi “catch lohkoon.” Tämä lohko on sisennettynä except lauseen alle rivillä 4. Tämä kohta huolehtii virheen käsittelystä.
Ohjelmassa voidaan käsitellä poikkeuksella myös tekstityyppisen muuttujan muuntaminen lukutyyppiin. Esimerkiksi:
# Invalid number conversion try: x = int("fred") except: print("Error converting fred to a number")
POikkeus nostetaan rivillä 3, jossa virheellisesti merkkijono 'fred' muunnetaan kokonaisluvuksi. Rivillä 5 tulostetaan virheilmoitus.
Alla on laajempi esimerkki virheenkäsittelystä. Virheentarkistuksessa tutkitaan, että käyttäjä syöttää kelvollisen kokonaisluvun. Jos sopivaa kokonaislukua ei syötetä, pyydetään kelvollista lukua. Koodissa nostetaan poikkeus rivillä 5, jos käyttäjä syöttää epäkelvollisen kokonaisluvun. Poikkeus käsitellään except-lohkossa ja lukua pyydetään uudelleen. Rivillä 6 olevaa totuusarvoista asetusta number_entered arvoon True ei tehdä, jos suoritus keskeytyy poikkeukseen rivillä 5.
number_entered = False while not number_entered: number_string = input("Enter an integer: ") try: n = int(number_string) number_entered = True except: print("Error, invalid integer")
Tiedostot ovat erityisen virheherkkiä tiedostotoiminnoissa. Levytila voi täyttyä, tiedosto voidaan poistaa samaan aikaan kuin sitä kirjoitetaan, tai se on voitu siirtää. USB-muisti on saatettu irrottaa virheellisesti kesken operaation. Tällaiset virheet voidaan helposti käsitellä poikkeusten käsittelyllä.
# Error opening file try: my_file = open("myfile.txt") except: print("Error opening file")
Monet eri virheet voidaan käsitellä monella eri tavalla. Tästä syystä myös virheilmoitukset voidaan muotoilla monipuolisemmin kuin yksinkertaisesti ilmoittamalla “an error has occurred.”
Alla olevassa koodissa on erilaisia virhetilanteita riveillä 5-8. Asettamalla IOError jälkeen except sanan rivillä 9, käsitellään ainoastaan Input/Output (IO) virheet tässä kohtaa. Samalla tapaa rivillä 11 käsitellään ainoastaan arvojen muunnoksissa tapahtuvat virheet, rivillä 13 huolehditaan mollalla jakovirheet. Rivillä 15 käsitellään yleisesti kaikki muut virheet, joita ei edellä käsitelty. “catch-all” except osan tulee olla aina viimeisenä.
Rivillä 1 ladataan sys kirjasto, jota tarvitaan rivillä 16 virheen tyypin tulostamiseen.
import sys # Multiple errors try: my_file = open("myfile.txt") my_line = my_file.readline() my_int = int(s.strip()) my_calculated_value = 101 / my_int except IOError: print("I/O error") except ValueError: print("Could not convert data to an integer.") except ZeroDivisionError: print("Division by zero error") except: print("Unexpected error:", sys.exc_info()[0])
Sisäänrakennettujen virheilmoitusten kuvaukset voit lukea tältä nettisivulta:
http://docs.python.org/library/exceptions.html
18.3 Esimerkki: High Scoren tallennus
Tässä on esimerkki pelin 'high scoren' tallentamisesta tekstitiedostoon high_score.txt.
""" Show how to use exceptions to save a high score for a game. Sample Python/Pygame Programs Simpson College Computer Science http://programarcadegames.com/ http://simpson.edu/computer-science/ """ def get_high_score(): # Default high score high_score = 0 # Try to read the high score from a file try: high_score_file = open("high_score.txt", "r") high_score = int(high_score_file.read()) high_score_file.close() print("The high score is", high_score) except IOError: # Error reading file, no high score print("There is no high score yet.") except ValueError: # There's a file there, but we don't understand the number. print("I'm confused. Starting with no high score.") return high_score def save_high_score(new_high_score): try: # Write the file to disk high_score_file = open("high_score.txt", "w") high_score_file.write(str(new_high_score)) high_score_file.close() except IOError: # Hm, can't write it. print("Unable to save the high score.") def main(): """ Main program is here. """ # Get the high score high_score = get_high_score() # Get the score from the current game current_score = 0 try: # Ask the user for his/her score current_score = int(input("What is your score? ")) except ValueError: # Error, can't turn what they typed into a number print("I don't understand what you typed.") # See if we have a new high score if current_score > high_score: # We do! Save to disk print("Yea! New high score!") save_high_score(current_score) else: print("Better luck next time.") # Call the main function, start up the game if __name__ == "__main__": main()
18.4 Poikkeuksen olio
Poikkeuksen nostamisessa saadaan aikaiseksi myös poikkeusolio. Tämä objekti saadaan käsiteltäväksi poikkeuksen nostossa as varatulla sanalla. Esimerkiksi:
try: x = 5 / 0 except ZeroDivisionError as e: print(e)
Muuttuja e sisältää tulostettavaa tietoa poikkeuksesta. Enemmänkin poikkeusolion avulla voitaisiin tehdä, mutta tässä kappaleessa ei asiaan syvennytä enempää. Lisää tietoa poikkeusoliosta saat vaikkapa Python-dokumentaatiosta.
18.5 Poikkeuksen generointi
Poikkeus voidaan luoda haluttuun tilanteeseen raise komennolla. Esimerkiksi:
# Generating exceptions def get_input(): user_input = input("Enter something: ") if len(user_input) == 0: raise IOError("User entered nothing") getInput()
Kokeile yllä olevaa koodia ja muuta virheilmoitusta. Näin huomaat, miten IOError virheen nosto toimii.
Myös omien poikkeusten luominen on mahdollista, mutta tämäkin asia
ei ole tämän materiaalin asia. Asiasta voit lukea enemmän seuraavalta nettisivulta:
http://docs.python.org/tutorial/errors.html#raising-exceptions
18.6 Proper Exception Use
Poikkeusten käsittelyä ei tarvita, varsinkaan jos tilanne voidaan hoitaa yksinkertaisesti if lauseella. Tavallisen toimivan ohjelmakoodin ei tulisi nostaa virheitä, varsinkin jos edetään niin sanotusti “onnellista polkua” virheitä välttäen. Hyvin konstruoidut 'try/catch' rakenteet on koodin luettaviuuden kannalta vielä hallittavissa, mutta koodi, jossa on useita eri virheenkäsittelijöitä ja hyppyjä eri kohtiin, saattaa olla yhtä painajaista debugata ja lukea.
18.7 Review
18.7.1 Multiple Choice Quiz
Klikkaa tästä monivalintatehtävään.
18.7.2 Short Answer Worksheet
Klikkaa tästä kappaleen kertaustehtäviin.
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin