Enhavolisto

Antaŭa temo

12. Klasoj

Sekva temo

14. Nomospacoj kaj valorizado

13. Eraroj kaj esceptoj

Ĝis nun, eraro mesaĝoj estis nur menciitaj.

Estas almenaŭ du malsamaj tipoj de eraroj: sintaksaj eraroj kaj esceptoj.

13.1. Sintaksaj eraroj

Sintaksaj eraroj estas la plej oftaj eraroj ricevitaj kiam oni ankoraŭ lernas Pitonon:

>>> while true print 'Saluton, mondo'
 File "<stdin>", line 1
  while true print 'Saluton, mondo'
            ^
SyntaxError: invalid syntax

La sintaksilo ripetas la linion kiu havas la eraron kaj videbligas etan ‘sagon’, kiu indikas la plej fruan punkton en la linio kie eraro estis trovita. La eraro estas trovita je la aĵo antaŭ la sago: en la ekzemplo, la eraro estas trovita je la prafunkcio print, ĉar dupunkto (:) mankas antaŭ ĝi. Dosiera nomo kaj linia numero estas videbligitaj por ke oni povas trovi linion se ĝi estas en skripto.

13.2. Esceptoj

Eĉ kiam instrukcio estas sintakse ĝusta, eraro eble okazas dum ĝia plenumado. Eraroj dum plenumado oni nomas esceptojn kaj ne devas esti fatalaj; oni baldaŭ lernos procedojn por trakti ilin en Pitonaj programoj. Se programo ne havas kodon por kapti kaj trakti esceptojn, rezulto estas erara mesaĝo.

Jen ekzemplo:

>>> 10 * (1/0)
Traceback (innermost last):
 File "<stdin>", line 1
ZeroDivisionError: integer division or modulo
>>> 4 + spamo*3
Traceback (innermost last):
 File "<stdin>", line 1
NameError: name 'spamo' is not difined
>>> '2' + 2
Traceback (innermost last):
 File "<stdin>", line 1
TypeError: illegal argument type for built-in operation

La lasta linio de la erara mesaĝo diras tion kio okazis. Estas diversaj tipoj da esceptoj, kaj la tipo estas videbligita kiel parto de la mesaĝo. La tipoj en la ekzemploj estas nuldivida eraro ZeroDivisionError, noma eraro NameError kaj tipa eraro TypeError. Pitono enmetas la nomon de la escepto en la eraran linion por ĉiuj primitivaj esceptoj, se ne nepre por esceptoj difinita de la programisto (tamen estas utila konvencio).

La cetero de la erara linio enhavas detalojn, kies signifon dependas de la escepta tipo.

La antaŭa parto de la mesaĝo videbligas la situacion en kiu la escepto okazis, en la formo de staka retrospuro. Ĝenerale, ĝi enhavas stakan retrospuron kiu listas kodoliniojn; tamen ne videbligas liniojn kiu estis legita el norma enigilo.

La Python Library Reference listigas la primitivajn esceptojn kaj iliajn signifojn.

13.3. Trakti esceptojn

Oni povas skribi programojn kiuj traktas elektitajn esceptojn. Rigardu la jena ekzemplo, kiu videbligas tabelon de inversoj de iu glitpunktaj numeroj:

>>> numeroj = [0.3333, 2.5, 0, 10]
>>> for a in numeroj:
...     print a,
...     try:
...        print 1.0 / a
...     except ZeroDivisionError:
...        print '*** ne havas inverson ***'
...
0.3333 3.00030003
2.5 0.4
0 *** ne havas inverson ***
10 0.1

La instrukcio try funkcias jene:

  1. Unue, la kodogrupo try (la instrukcioj inter la ŝlosilvortoj try kaj except) estas lanĉita.
  2. Se escepto ne okazas, la kodogrupo except estas ignorita kaj la kodogrupo try finiĝos.

Se escepto okazas en la kodogrupo try, la cetero de la kodogrupo try estas ignorita. Se la tipo de escepto respondas al la escepto nomita post la primitiva vorto except, la kodogrupo except estas lancita, tiam la instrukcioj post la tuta grupo try-except estas lanĉitaj.

Oni povas nesti grupojn try-except. Se escepto okazas, kaj ne respondas al la escepto nomita en la kodogrupo except, la escepto estas sendita laŭvice al ĉiu pli altnivela kodogrupo try; se ne estas responda kodogrupo except, eraro okazas kaj la programo ĉesas funkcii. Eraro mesaĝo estas videbligita.

La instrukcio try povas havi pli ol unu kodogrupon except, por trakti diversajn esceptojn. Maksimume, unu kodogrupo except estos lanĉita. La kodogrupo except nur traktas esceptojn kiu okazis en la kodogrupo try, ne tiuj kiuj okazas en alia(j) kodogrupo(j) except. Escepto povas nomi multajn esceptojn en enkrampa listo, ekz.:

... except (RuntimeError, TypeError, NameError):
...     <escepta kodo>

La lasta kodogrupo except povas ellasi nomo(j)n de esceptoj, do nomo estas ĵokero (do respondas al iu ajn escepto) Uzu ĉi tiun ilon zorge, ĉar oni facile kaŝi veran programan eraron per tio! Oni povas presi eraran mesaĝon kaj poste relevi la escepton.

import string, sys   try:
    f = open('miadosiero.txt')
    s = f.readline()
    i = int(s.strip())
except IOError, (errno, strerror):
    print "en/el eraro(%s): %s" % (errno, strerror)
except ValueError:
    print "Ne povas konverti al entjero."
except:
    print "Neanticipita eraro:", sys.exc_info()[0]
    raise #relevi escepton (vidu 'Levi Escepton' sube)

La instrukcio try ... except havas nedevigan kodogrupon else, kiu devas sekvi la lastan kodogrupon except. La kodogrupo else estas lanĉita se la kodogrupo try ne levas escepton. Ekzemple:

for arg in sys.argv:
try:
    f = open(arg, 'r')
except IOError:
    print 'ne povas malfermi', arg
else:
    print arg, 'havas', len(f.readlines()),'liniojn'
    f.close()

Kiam escepto okazas, ĝi eble resendas valoron, nomita la argumento de la escepto. La ekzisto kaj tipo de la argumento dependas de la tipo de escepto.Por esceptoj kiu resendas argumentojn, variablo post la escepta nomo (aŭ listo) ricevos argumenton. Oportune, la eroj _getitem_ kaj _str_ enhavas la informon de la argumenton.:

>>> try:
...     raise Exception('spamo', 'ovoj')
... except Exception, kazo:
...     print type(inst)# la escepta kazo
...     print inst.args # argumentoj metita en .args
...     print inst      # per __str__, presu arg-ojn rekte
...     a, b = inst     # per __getitem__, malpaku arg-ojn
...     print 'a =', a
...     print 'b =', b
...
<type 'instance'>
('spamo', 'ovoj')
('spamo', 'ovoj')
a = spamo
b = ovoj

La kodogrupo except ne nur traktas esceptojn kiuj okazas tuj en kodogrupo try, sed ankaŭ esceptojn kiuj okazas interne de funkcioj vokitaj (eĉ nerekte) de la kodogrupo try. Por ekzemplo:

>>> def ne_sukcesas():
...     a = 1/0
...
>>> try:
...     ne_sukcesas()
... except ZeroDivisionError, detalo:
...     print 'Dumplenuma eraro:', detalo
...
Dumplenuma eraro: integer division or modulo

13.4. Levi esceptojn

Oni uzas la instrukcion raise por okazigi specifan escepton. Por ekzemplo:

>>> raise NameError, 'Hola'
Traceback (most recent call last):
 File "<stdin>", line 1 in ?
NameError: Hola

La unua argumento de la instrukcio raise nomas la escepton levotan. La nedeviga dua argumento estas la valoro resendota de la escepto.

Se oni volas nur rimarki la escepton, oni simple relevos la escepton:

>>> try:
...     raise NameError, 'Hola'
... except NameError:
...     print 'Escepto okazis - netraktita!'
...     raise
...
Escepto okazis - netraktita!
Traceback (most recent call last):
 File "<stdin>", line 2, in ?
NameError: Hola

13.5. Uzanto-difinitaj esceptoj

Programisto povas nomi ilian propran escepton per kreado de nova escepto-klaso, Oni devus derivi esceptojn de klaso Exceptions aŭ rekte aŭ nerekte. Ekzemple:

>>> class MiaEraro(Exception):
...     def __init__(self, valoro):
...         self.valoro = valoro
...     def __str__(self):
...         return repr(self.valoro)
...
>>> try:
...     raise MiaEraro(2*2)
... except MiaEraro, e:
...     print 'Mia escepto okazis, valoro:', e.valoro
...
Mia escepto okazis, valoro: 4
>>> raise MiaEraro, 'oj!'
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
__main__.MyError: 'oj!'

Esceptaj klasoj povas fari ĉion kion aliaj klasoj povas fari, sed kutime ili estas simplaj, provizante per atribuoj kiuj donas informon pri la eraro. Se diversaj eraoj eble okazus, ofte oni kreas bazan klason por eblaj esceptoj, kaj poste fari specifajn subklasojn por ĉiu tipo de eraro.

klaso Eraro(Exception):
    """Baza klaso por esceptoj en ĉi tiu modulo."""
    pass

klaso EnigEraro(Eraro):
    """Escepto levigita por enigaj eraroj.

    Atribuoj:
        esprimo -- eniga esprimo kie la eraro okazis
        mesaĝo -- klarigo de la eraro    """

    def __init__(mem, esprimo, mesaĝo):
        mem.esprimo = esprimo
        mem.mesaĝo = mesaĝo

Oni difinas la plejparton de esceptoj per nomoj kiuj finas per la vorto ‘eraro’, simile al la praesceptoj.

13.6. Difino de purigaj agadoj

La instrukcio try havas alian nedevigan kodogrupon kiu difinas purigajn agadojn ĉiam farotajn. Ekzemple:

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print 'ĝis la, mondo!'
...
ĝis la, mondo!
Traceback (innermost last):
 File "<stdin>", line 2
KeyboardInterrupt

La kodogrupo finally estas lanĉita ĉu escepto okazis en kodogrupo try aŭ ne. Kiam escepto okazas, ĝi estas relevita post la kodogrupo finally finiĝos. La kodogrupo finally ankaŭ estas lanĉita je la punkto kiam programo saltas el la kodogrupo try pro instrukcio breakreturn.

La instrukcio try devas havi aŭ kodogrupo(j)n except aŭ instrukcion finally, sed ne ambaŭ.