ΜΥΥ105: Εισαγωγή στον Προγραµµατισµό Σφάλµατα, εξαιρέσεις, εκσφαλµάτωση Χειµερινό Εξάµηνο 2014
Τύποι Σφαλµάτων Συντακτικά λάθη (syntax errors) Λάθη κατά την εκτέλεση (run-time errors) Λογικά σφάλµατα 2
Συντακτικά Λάθη Είναι σηµαντικό να καταλάβουµε το συντακτικό λάθος και να το διορθώσουµε break.py from math import sqrt for n in range(99, 0, -1): root = sqrt(n) if root == int(root): print (n) break python3 break.py File "break.py", line 6 break ^ IndentationError: unindent does not match any outer indentation level 3
Συντακτικά Λάθη Είναι σηµαντικό να καταλάβουµε το συντακτικό λάθος και να το διορθώσουµε test.py suits = ['\u2660', '\u2661', '\u2662', '\u2663'] print((suits) x = 5 python3 test.py File "test.py", line 3 x = 5 ^ SyntaxError: invalid syntax 4
Συντακτικά Λάθη Είναι σηµαντικό να καταλάβουµε το συντακτικό λάθος και να το διορθώσουµε >>> (3+4] SyntaxError: invalid syntax >>> if x==5 SyntaxError: invalid syntax >>> print 'hello' SyntaxError: invalid syntax >>> lst = [1;2;3] SyntaxError: invalid syntax >>> for i in range(10): print(i) SyntaxError: expected an indented block 5
Λάθη κατά την εκτέλεση Δεν υπάρχει συντακτικό λάθος, αλλά το το πρόγραµµά µας τερµατίζει γιατί µπαίνει σε µια µη εγκυρη κατάσταση. >>> 4/0 Traceback (most recent call last): File "<pyshell#43>", line 1, in <module> 4/0 ZeroDivisionError: division by zero >>> lst = [1,2,3] >>> lst[3] Traceback (most recent call last): File "<pyshell#45>", line 1, in <module> lst[3] IndexError: list index out of range 6
Λάθη κατά την εκτέλεση Πρέπει να διαβάσουµε και να καταλάβουµε το σφάλµα >>> y + 5 Traceback (most recent call last): File "<pyshell#46>", line 1, in <module> y + 5 NameError: name 'y' is not defined >>> '2' * '3' Traceback (most recent call last): File "<pyshell#47>", line 1, in <module> '2' * '3' TypeError: can't multiply sequence by non-int of type 'str' 7
Λάθη κατά την εκτέλεση Πρέπει να διαβάσουµε και να καταλάβουµε το σφάλµα >>> int('4.5') Traceback (most recent call last): File "<pyshell#48>", line 1, in <module> int('4.5') ValueError: invalid literal for int() with base 10: '4.5' >>> 2.0**10000 Traceback (most recent call last): File "<pyshell#49>", line 1, in <module> 2.0**10000 OverflowError: (34, 'Result too large') 8
Εξαιρέσεις Όταν το πρόγραµµα µπει σε µια µη έγκυρη κατάσταση δηµιουργεί µια εξαίρεση (exception) Η εξαίρεση είναι κι αυτή αντικείµενο Κάθε εξαίρεση ανήκει σε ένα τύπο Ο τύπος µας βοηθάει να καταλάβουµε το σφάλµα Εξαίρεση KeyboardInterrupt OverflowError Εξήγηση Ο χρήστης διέκοψε το πρόγραµµα µε Crtl-C Ένας float πήρε πολύ µεγάλη τιµή ZeroDivisionError Διαίρεση µε το 0 FileNotFoundError IndexError NameError TypeError ValueError Το όνοµα αρχείου που επιχειρούµε να ανοίξουµε δεν υπάρχει Προσπαθήσαµε να προσπελάσουµε µια µη έγκυρη θέση ακολουθίας Προσπέλαση µεταβλητής που δεν έχει οριστεί Μια λειτουργία εφαρµόζεται σε λάθος τύπο αντικειµένου Μια λειτουργία εφαρµόζεται σε µη έγκυρη τιµή 9
Δηµιουργία Εξαιρέσεων Μπορούµε να δηµιουργήσουµε εξαίρεση στο πρόγραµµα ή τη συνάρτησή µας αν κάτι δεν είναι έγκυρο µε βάση τις προδιαγραφές µας µε την εντολή raise x = int(input('input a number between 10 and 20: ')) if x<10 or x>20: raise Exception('Number outside valid range') print(x**2) περιγραφή της εξαίρεσης Input a number between 10 and 20: 12 144 Input a number between 10 and 20: 3 Traceback (most recent call last): File "test.py", line 3, in <module> raise Exception('Number outside valid range') Exception: Number outside valid range 10
«Πιάσιµο» Εξαιρέσεων Μπορούµε να αποφύγουµε να τερµατίσει το πρόγραµµά µας, φροντίζοντας να «πιάνουµε» τυχόν εξαιρέσεις try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) print (x/y) except ZeroDivisionError: print("the second number can't be zero!") print("program finished normally...") Enter the first number: 3 Enter the second number: 4 0.75 program finished normally... 11
«Πιάσιµο» Εξαιρέσεων Μπορούµε να αποφύγουµε να τερµατίσει το πρόγραµµά µας, φροντίζοντας να «πιάνουµε» τυχόν εξαιρέσεις try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) print (x/y) except ZeroDivisionError: print("the second number can't be zero!") print("program finished normally...") Enter the first number: 3 Enter the second number: 0 The second number can't be zero! program finished normally... 12
Αλλά... try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) print (x/y) except ZeroDivisionError: print("the second number can't be zero!") print("program finished normally...") Enter the first number: 3a Traceback (most recent call last): File "/Users/Nikos/Dropbox/courses/course_python/py/test.py", line 2, in <module> x = eval(input('enter the first number: ')) File "<string>", line 1 3a ^ SyntaxError: unexpected EOF while parsing 13
«Πιάσιµο» Εξαιρέσεων try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) print (x/y) except ZeroDivisionError: print("the second number can't be zero!") except NameError: print("you did not give a number") except SyntaxError: print("you did not give a number ) οι εξαίρεσεις εξετάζονται µε αυτή τη σειρά 14
«Πιάσιµο» Εξαιρέσεων try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) print (x/y) except ZeroDivisionError: print("the second number can't be zero!") except: print("something wrong happened") αν δεν προσδιορίσουµε συγκεκριµένο τύπο εξαίρεσης µετά το except, τότε εννοούµε οποιαδήποτε εξαίρεση το σκέτο except πρέπει να µπαίνει µε προσοχή γιατί πιάνει τα πάντα (ακόµα και το Ctrl-C) 15
«Πιάσιµο» Εξαιρέσεων try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) print (x/y) except (ZeroDivisionError, NameError, SyntaxError): print("something wrong with your numbers") µπορούµε να βάλουµε πολλούς τύπους εξαιρέσεων 16
«Πιάσιµο» Εξαιρέσεων try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) print (x/y) except (ZeroDivisionError, NameError, SyntaxError) as e: print(e) Enter the first number: 3a unexpected EOF while parsing (<string>, line 1) αποθηκεύει το περιεχόµενο της εξαίρεσης (ένα αλφαριθµητικό) στη µεταβλητή e 17
Χρήση try/except/else while True: try: x = eval(input('enter the first number: ')) y = eval(input('enter the second number: ')) value = x/y print ('x/y is', value) except Exception as e: print('invalid input: ',e) print('please try again.') else: break τυπώνουµε την εξαίρεση για να ξέρει ο χρήστης τι πήγε στραβά αν δε δηµιουργήθηκε εξαίρεση, τότε όλα πήγαν καλά και ο βρόγχος τερµατίζει 18
Παράδειγµα Γράψτε µια συνάρτηση που θα µετατρέπει την πρώτη γραµµή ενός αρχείου σε ακέραιο και µετά θα τυπώνει τον ακέραιο def readage(filename): try: infile = open(filename) strage = infile.readline() age = int(strage) print('age is',age) except IOError: print('input/output error.') except ValueError: print('value cannot be converted to integer.') except: print('other error.') 19
Εξαιρέσεις και συναρτήσεις Αν δηµιουργηθεί εξαίρεση (σφάλµα) µέσα σε µια συνάρτηση, τότε η συνάρτηση σταµατάει και επιστέφει Μπορούµε να «πιάσουµε» εξαιρέσεις µέσα στη συνάρτηση και να τις χειριστούµε Επίσης µπορούµε να «πιάσουµε» µια εξαίρεση που δηµιουργήθηκε σε µια συνάρτηση έξω από τη συνάρτηση Μια εξαίρεση που δηµιουργείται σε µια συνάρτηση, αν δεν τη χειριστούµε εκεί, «µεταφέρεται» στο σηµείο που καλείται η συνάρτηση, και µε τον ίδιο τρόπο µέχρι το κυρίως πρόγραµµα Αν δεν χειριστούµε την εξαίρεση κάπου, το πρόγραµµα τερµατίζει µε σφάλµα δείχνοντας όλα τα σηµεία από όπου πέρασε η εξαίρεση (στοίβα εκτέλεσης προγράµµατος). 20
Εξαιρέσεις και συναρτήσεις def faulty(): raise Exception('Something is wrong') def ignore_exception(): faulty() def handle_exception(): try: faulty() except: print('exception handled') >>> ignore_exception() Traceback (most recent call last): File "<pyshell#12>", line 1, in <module> ignore_exception() File "test.py", line 5, in ignore_exception faulty() File "test.py", line 3, in faulty raise Exception('Something is wrong') Exception: Something is wrong 21
Εξαιρέσεις και συναρτήσεις def faulty(): raise Exception('Something is wrong') def ignore_exception(): faulty() def handle_exception(): try: faulty() except: print('exception handled') >>> handle_exception() Exception handled 22
Εξαιρέσεις και συναρτήσεις 23
Παράδειγµα Έστω ότι αποθηκεύουµε τα στοιχεία προσώπων σαν λεξικά Αν ένα πρόσωπο δέν έχει κάποια στοιχεία, ή δεν τα γνωρίζουµε, απλά δεν υπάρχουν στο λεξικό του >>> john = {'age':34, 'name':'john'} >>> steve = {'age':23, 'occupation':'locksmith'} >>> john['age'] 34 >>> john['occupation'] Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> person['occupation'] KeyError: 'occupation' 24
Παράδειγµα Θέλουµε να γράψουµε µια συνάρτηση describeperson() που να τυπώνει τα υπάρχοντα στοιχεία του προσώπου που της δίνεται σαν όρισµα Το name είναι υποχρεωτικό κλειδί στο όρισµα της συνάρτησης, τα υπόλοια στοιχεία, προαιρετικά >>> john = {'age':34, 'name':'john'} >>> steve = {'name':'steve', 'occupation':'locksmith'} >>> describeperson(john) Description of John Age: 34 >>> describeperson(steve) Description of Steve Occupation: locksmith 25
Παράδειγµα Θέλουµε να γράψουµε µια συνάρτηση describeperson() που να τυπώνει τα υπάρχοντα στοιχεία του προσώπου που της δίνεται σαν όρισµα def describeperson(person): print('description of', person['name']) if 'age' in person: print('age:', person['age']) if 'occupation' in person: print('occupation:', person['occupation']) if 'marital' in person: print('marital Status:', person['marital']) Βάζουµε ένα if για κάθε πιθανό στοιχείο! 26
Παράδειγµα Θέλουµε να γράψουµε µια συνάρτηση describeperson() που να τυπώνει τα υπάρχοντα στοιχεία του προσώπου που της δίνεται σαν όρισµα Με χρήση εξαιρέσεων: def describeperson(person): print('description of', person['name']) try: print('age:', person['age']) except KeyError: pass try: print('occupation:', person['occupation']) except KeyError: pass try: print('marital Status:', person['marital']) except KeyError: pass 27
Αποσφαλµάτωση Αν το πρόγραµµά µας δε λειτουργεί σωστά (π.χ. έχει λογικό σφάλµα) και δεν ξέρουµε που είναι το σφάλµα, πρέπει να χρησιµοποιήσουµε µια µέθοδο εκσφαλµάτωσης (debugging). Η εκσφαλµάτωση µπορεί να γίνει µε ειδικά προγράµµατα (debuggers) Ο debugger προσοµοιώνει την εκτέλεση του προγράµµατος εντολή-προς-εντολή και δίνει ανά πάσα στιγµή τις τιµές των µεταβλητών και τη στοίβα του προγράµµατος Τα περισσότερα IDE (integrated development interfaces) εµπεριέχουν debugger 28
Αποσφαλµάτωση Τυπική µεθοδολογία εκσφαλµάτωσης: Επανάληψη του προγράµµατος (ώστε να εντοπιστεί το σφάλµα) Αποµόνωση του σηµείου που εµφανίζεται το σφάλµα Αναγνώριση της αιτίας που το προκαλεί (π.χ. από απροσεξία ή από λογικό σφάλµα) Διόρθωση του σφάλµατος Επιβεβαίωση της διόρθωσης (επανάληψη του προγράµµατος, δοκιµή µε διαφορετικές εισόδους) Τα παραπάνω βήµατα είναι τυπικά. Κάποιοι προγραµµατιστές µπορεί να ακολουθούν µια παραλλαγή της µεθοδολογίας αυτής 29
Συµβουλές για την πρόληψη σφαλµάτων Σχεδιάστε πρώτα τον αλγόριθµό σας στο χαρτί και βεβαιωθείτε ότι δεν έχει λογικά λάθη Γράφετε το πρόγραµµά σας λίγο-λίγο και εκτελείτε το δοκιµάζοντας την ορθότητά του (π.χ. µε εντολές print) πριν προχωρήσετε στο να γράψετε τις επόµενες γραµµές του Δοκιµάζετε πάντα την ορθότητα µε µια ποικιλία δεδοµένων εισόδου και βεβαιωθείτε ότι είναι σωστό για όλες τις πιθανές εισόδους Κάντε χρήση συναρτήσεων ώστε να σπάσετε το πρόγραµµα σε µικρά υπο-προγράµµατα τα οποία είναι ευκολότερο να ελεγχθούν Εισάγετε σχόλια ώστε να µπορέσετε να θυµηθείτε τη λειτουργία τµηµάτων κώδικα που θα πρέπει να αλλάξετε σε περίπτωση σφάλµατος 30