Λεξικά

Ένα λεξικό (dictionary) είναι σαν μια λίστα, αλλά πιο γενικό. Σε μια λίστα, οι δείκτες θέσης πρέπει να είναι ακέραιοι, ενώ σε ένα λεξικό, οι δείκτες μπορούν να είναι (σχεδόν) οποιουδήποτε τύπου.

Μπορείτε να σκεφτείτε ένα λεξικό ως μια αντιστοίχιση μεταξύ ενός συνόλου δεικτών (που ονομάζονται κλειδιά) και ενός συνόλου τιμών. Κάθε κλειδί αντιστοιχίζεται σε μια τιμή. Η συσχέτιση ενός κλειδιού και μιας τιμής ονομάζεται ζεύγος κλειδιού-τιμής (key-value pair) ή μερικές φορές στοιχείο.

Για παράδειγμα, θα δημιουργήσουμε ένα λεξικό που αντιστοιχίζει λέξεις από τα αγγλικά στα ισπανικά, έτσι ώστε τα κλειδιά και οι τιμές να είναι όλα συμβολοσειρές.

Η συνάρτηση dict δημιουργεί ένα νέο λεξικό χωρίς στοιχεία. Επειδή το dict είναι το όνομα μιας ενσωματωμένης συνάρτησης, θα πρέπει να αποφύγετε τη χρήση του ως όνομα μεταβλητής.

>>> eng2sp = dict()
>>> print(eng2sp)
{}

Τα άγκυστρα, {}, αντιπροσωπεύουν ένα κενό λεξικό. Για να προσθέσετε στοιχεία στο λεξικό, μπορείτε να χρησιμοποιήσετε αγκύλες:

>>> eng2sp['one'] = 'uno'

Αυτή η γραμμή δημιουργεί ένα στοιχείο που αποτελείται από το κλειδί 'one' και την τιμή “uno”. Εάν εκτυπώσουμε ξανά το λεξικό, βλέπουμε ένα ζεύγος κλειδιού-τιμής με άνω και κάτω τελεία μεταξύ του κλειδιού και της τιμής:

>>> print(eng2sp)
{'one': 'uno'}

Αυτή η μορφή εξόδου είναι επίσης μια μορφή εισόδου. Για παράδειγμα, μπορείτε να δημιουργήσετε ένα νέο λεξικό με τρία στοιχεία. Αλλά αν εκτυπώσετε «eng2sp», ίσως εκπλαγείτε:

>>> eng2sp = {'one': 'uno', 'two': 'dos', 'three': 'tres'}
>>> print(eng2sp)
{'one': 'uno', 'three': 'tres', 'two': 'dos'}

Η σειρά των ζευγών κλειδιού-τιμής δεν είναι η ίδια. Στην πραγματικότητα, αν πληκτρολογήσετε το ίδιο παράδειγμα στον υπολογιστή σας, ενδέχεται να έχετε διαφορετικό αποτέλεσμα. Γενικά, η σειρά των στοιχείων σε ένα λεξικό είναι απρόβλεπτη.

Αλλά αυτό δεν είναι πρόβλημα γιατί τα στοιχεία ενός λεξικού δεν χρησιμοποιούν ποτέ ευρετήριο με ακέραιους δείκτες. Αντίθετα, χρησιμοποιείτε τα κλειδιά για να αναζητήσετε τις αντίστοιχες τιμές:

>>> print(eng2sp['two'])
'dos'

Το κλειδί 'two' αντιστοιχεί πάντα στην τιμή “dos”, οπότε η σειρά των στοιχείων δεν έχει σημασία.

Εάν το κλειδί δεν υπάρχει στο λεξικό, λαμβάνετε μια εξαίρεση:

>>> print(eng2sp['four'])
KeyError: 'four'

Η συνάρτηση len λειτουργεί σε λεξικά. Επιστρέφει το πλήθος των ζευγών κλειδιού-τιμής:

>>> len(eng2sp)
3

Ο τελεστής in λειτουργεί σε λεξικά. Σας λέει εάν κάτι εμφανίζεται ως κλειδί στο λεξικό (η εμφάνιση ως τιμή δεν αρκεί).

>>> 'one' in eng2sp
True
>>> 'uno' in eng2sp
False

Για να δείτε εάν κάτι εμφανίζεται ως τιμή σε ένα λεξικό, μπορείτε να χρησιμοποιήσετε τη μέθοδο values, η οποία επιστρέφει τις τιμές ως έναν τύπο dict_values, που μπορεί να μετατραπεί σε λίστα και, στη συνέχεια, μπορείτε να χρησιμοποιήσετε τον τελεστή in (την Python 2 η μέθοδος values επιστρέφει μία λίστα των τιμών):

>>> vals = list(eng2sp.values())
>>> 'uno' in vals
True

Ο τελεστής in χρησιμοποιεί διαφορετικούς αλγόριθμους για λίστες και για λεξικά. Για λίστες, χρησιμοποιεί έναν αλγόριθμο γραμμικής αναζήτησης. Καθώς η λίστα μεγαλώνει, ο χρόνος αναζήτησης μεγαλώνει σε ευθεία αναλογία με το μήκος της λίστας. Για τα λεξικά, η Python χρησιμοποιεί έναν αλγόριθμο που ονομάζεται πίνακες κατακερµατισµού (hash table) που έχει μια αξιοσημείωτη ιδιότητα: ο τελεστής in χρειάζεται περίπου τον ίδιο χρόνο ανεξάρτητα από το πόσα στοιχεία περιέχονται σε ένα λεξικό. Δεν θα εξηγήσω γιατί οι συναρτήσεις κατακερματισμού είναι τόσο μαγικές, αλλά μπορείτε να διαβάσετε περισσότερα για αυτό στο wikipedia.org/wiki/Hash_table.

Άσκηση 1: Κατεβάστε ένα αντίγραφο του αρχείου www.gr.py4e.com/code3/words.txt

Γράψτε ένα πρόγραμμα που να διαβάζει τις λέξεις στο words.txt και να τις αποθηκεύει ως κλειδιά σε ένα λεξικό. Δεν έχει σημασία ποιες είναι οι τιμές. Στη συνέχεια, μπορείτε να χρησιμοποιήσετε τον τελεστή in ως έναν γρήγορο τρόπο για να ελέγξετε εάν μια συμβολοσειρά υπάρχει στο λεξικό.

Το λεξικό ως σύνολο μετρητών

Ας υποθέσουμε ότι σας δίνεται μια συμβολοσειρά και θέλετε να μετρήσετε πόσες φορές εμφανίζεται κάθε γράμμα. Υπάρχουν διάφοροι τρόποι για να το κάνετε:

  1. Θα μπορούσατε να δημιουργήσετε 26 μεταβλητές, μία για κάθε γράμμα του αλφαβήτου. Στη συνέχεια, θα μπορούσατε να διασχίσετε τη συμβολοσειρά και, για κάθε χαρακτήρα, να αυξήσετε τον αντίστοιχο μετρητή, πιθανώς χρησιμοποιώντας μια πολλαπλή συνθήκη.

  2. Θα μπορούσατε να δημιουργήσετε μια λίστα με 26 στοιχεία. Στη εια, να μετατρέψετε κάθε χαρακτήρα σε έναν αριθμό (χρησιμοποιώντας την ενσωματωμένη συνάρτηση ord), να χρησιμοποιήσετε τον αριθμό ως δείκτη στη λίστα και να αυξήσετε τον κατάλληλο μετρητή.

  3. Θα μπορούσατε να δημιουργήσετε ένα λεξικό με χαρακτήρες ως κλειδιά και μετρητές ως τις αντίστοιχες τιμές. Την πρώτη φορά που εντοπίζετε έναν χαρακτήρα, προσθέτετε ένα στοιχείο στο λεξικό. Στη συνέχεια θα αυξάνετε την τιμή του υπάρχοντος στοιχείου.

Κάθε μία από αυτές τις επιλογές εκτελεί τον ίδιο υπολογισμό, αλλά καθεμία από αυτές υλοποιεί τον υπολογισμό με διαφορετικό τρόπο.

Μια υλοποίηση είναι ένας τρόπος εκτέλεσης ενός υπολογισμού. Ορισμένες υλοποιήσεις είναι καλύτερες από άλλες. Για παράδειγμα, ένα πλεονέκτημα της εφαρμογής του λεξικού είναι ότι δεν χρειάζεται να γνωρίζουμε εκ των προτέρων ποια γράμματα εμφανίζονται στη συμβολοσειρά και δημιουργούμε θέσεις μόνο για τα γράμματα που εμφανίζονται.

Εδώ είναι µια πιθανή εκδοχή του κώδικα:

word = 'brontosaurus'
d = dict()
for c in word:
    if c not in d:
        d[c] = 1
    else:
        d[c] = d[c] + 1
print(d)

Υπολογίζουμε ουσιαστικά ένα ιστόγραμμα, το οποίο είναι ένας στατιστικός όρος για ένα σύνολο μετρητών (ή συχνοτήτων).

Ο βρόχος for διασχίζει τη συμβολοσειρά. Κάθε φορά μέσω του βρόχου, εάν ο χαρακτήρας c δεν υπάρχει στο λεξικό, δημιουργούμε ένα νέο στοιχείο με το κλειδί c και την αρχική τιμή 1 (αφού έχουμε δει αυτό το γράμμα μία φορά). Εάν το c είναι ήδη στο λεξικό, αυξάνουμε το d[c].

Ακολουθεί η έξοδος του προγράμματος:

{'a': 1, 'b': 1, 'o': 2, 'n': 1, 's': 2, 'r': 2, 'u': 2, 't': 1}

Το ιστόγραμμα δείχνει ότι τα γράμματα “a” και “b” εμφανίζονται από μία φορά. Το “o” εμφανίζεται δύο φορές και ούτω καθεξής.

Τα λεξικά έχουν μια μέθοδο που ονομάζεται get, που παίρνει ένα κλειδί και μια προεπιλεγμένη τιμή. Εάν το κλειδί εμφανίζεται στο λεξικό, το get επιστρέφει την αντίστοιχη τιμή, διαφορετικά επιστρέφει την προεπιλεγμένη τιμή. Για παράδειγμα:

>>> counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
>>> print(counts.get('jan', 0))
100
>>> print(counts.get('tim', 0))
0

Μπορούμε να χρησιμοποιήσουμε την get για να γράψουμε πιο συνοπτικά τον βρόχο ιστογράμματος. Επειδή η μέθοδος get χειρίζεται αυτόματα την περίπτωση που ένα κλειδί δεν περιέχεται στο λεξικό, μπορούμε να αντικαταστήσουμε τέσσερις γραμμές με μία και να εξαλείψουμε την εντολή if.

word = 'brontosaurus'
d = dict()
for c in word:
    d[c] = d.get(c,0) + 1
print(d)

Η χρήση της μεθόδου get, για την απλοποίηση αυτού του βρόχου μέτρησης καταλήγει σε ένα πολύ συχνά χρησιμοποιούμενο “ιδίωμα” στην Python που θα το χρησιμοποιήσουμε πολλές φορές στο υπόλοιπο βιβλίο. Θα πρέπει λοιπόν να αφιερώσετε λίγο χρόνο και να συγκρίνετε τον βρόχο που χρησιμοποιεί την εντολή if και τον τελεστή in με τον βρόχο που χρησιμοποιεί τη μέθοδο get. Κάνουν ακριβώς το ίδιο πράγμα, αλλά ο δεύτερος είναι συνοπτικότερος.

Λεξικά και αρχεία

Μία από τις συνησμένες χρήσεις ενός λεξικού είναι να μετράει την εμφάνιση λέξεων σε ένα αρχείο, που περιέχει κείμενο. Ας ξεκινήσουμε με ένα πολύ απλό αρχείο λέξεων, βγαλμένών από το κείμενο του Ρωμαίος και Ιουλιέτα (Romeo and Juliet).

Για το πρώτο σύνολο παραδειγμάτων, θα χρησιμοποιήσουμε μια σύντομη και απλοποιημένη έκδοση του κειμένου, χωρίς σημεία στίξης. Αργότερα θα δουλέψουμε με το κείμενο της σκηνής με σημεία στίξης.

But soft what light through yonder window breaks
It is the east and Juliet is the sun
Arise fair sun and kill the envious moon
Who is already sick and pale with grief

Θα γράψουμε ένα πρόγραμμα Python για να διαβάσουμε τις γραμμές του αρχείου, θα διασπάσουμε κάθε γραμμή σε μια λίστα λέξεων και, στη συνέχεια, με βρόχο θα διατρέξουμε κάθε μία από τις λέξεις της γραμμής και θα μετρήσουμε κάθε λέξη χρησιμοποιώντας ένα λεξικό.

Θα δείτε ότι έχουμε δύο βρόχους for. Ο εξωτερικός βρόχος διαβάζει τις γραμμές του αρχείου και ο εσωτερικός βρόχος διατρέχει κάθε μία από τις λέξεις στη συγκεκριμένη γραμμή. Αυτό είναι ένα παράδειγμα ενός μοτίβου που ονομάζεται εμφωλευμένοι βρόχοι επειδή ένας από τους βρόχους είναι ο εξωτερικός βρόχος και ο άλλος βρόχος είναι ο εσωτερικός.

Επειδή ο εσωτερικός βρόχος εκτελεί όλες τις επαναλήψεις του κάθε φορά που ο εξωτερικός βρόχος κάνει μία επανάληψη, θεωρούμε ότι ο εσωτερικός βρόχος επαναλαμβάνεται “πιο γρήγορα” και ο εξωτερικός βρόχος επαναλαμβάνεται πιο αργά.

Ο συνδυασμός των δύο εμφψλευμένων βρόχων διασφαλίζει ότι θα μετράμε κάθε λέξη, σε κάθε γραμμή του αρχείου εισόδου.

fname = input('Εισαγάγετε το όνομα του αρχείου: ')
try:
    fhand = open(fname)
except:
    print('Δεν είναι δυνατό το άνοιγμα του αρχείου:', fname)
    exit()

πλήθη = dict()
for γραμμή in fhand:
    λέξεις = γραμμή.split()
    for λέξη in λέξεις:
        if λέξη not in πλήθη:
            πλήθη[λέξη] = 1
        else:
            πλήθη[λέξη] += 1

print(πλήθη)

# Code: http://www.py4e.com/code3/count1.py

Στη δήλωση else, χρησιμοποιούμε την πιο συμπαγή εναλλακτική για την αύξηση μιας μεταβλητής. Το counts[word] += 1 ισοδυναμεί με counts[word] = counts[word] + 1. Και οι δύο τρόποι μπορούν να χρησιμοποιηθούν για την αλλαγή της τιμής μιας μεταβλητής κατά οποιοδήποτε, επιθυμητό, ποσό. Παρόμοιες εναλλακτικές υπάρχουν για τα -=, *= και /=.

Όταν εκτελούμε το πρόγραμμα, βλέπουμε μια ακατέργαστη έξοδο όλων των μετρήσεων, σε μη ταξινομημένη σειρά. (Το αρχείο romeo.txt είναι διαθέσιμο στη διεύθυνση www.py4e.com/code3/romeo.txt)

python count1.py
Εισαγάγετε το όνομα του αρχείου: romeo.txt
{'and': 3, 'envious': 1, 'already': 1, 'fair': 1,
'is': 3, 'through': 1, 'pale': 1, 'yonder': 1,
'what': 1, 'sun': 2, 'Who': 1, 'But': 1, 'moon': 1,
'window': 1, 'sick': 1, 'east': 1, 'breaks': 1,
'grief': 1, 'with': 1, 'light': 1, 'It': 1, 'Arise': 1,
'kill': 1, 'the': 3, 'soft': 1, 'Juliet': 1}

Είναι λίγο άβολο να ψάξουμε μέσα στο λεξικό για να βρούμε τις πιο συχνά επαναλαμβανόμενες λέξεις και τον πλήθος των εμφανίσεών τους, γι’ αυτό πρέπει να προσθέσουμε λίγο ακόμα κώδικα Python για να έχουμε μια έξοδο που θα είναι πιο εύχρηστη.

Βρόχοι και λεξικά

Εάν χρησιμοποιείτε ένα λεξικό ως ακολουθία μιας εντολής for, τότε αυτή διασχίζει τα κλειδιά του λεξικού. Για παράδειγμα, αυτός ο βρόχος εκτυπώνει κάθε κλειδί και την αντίστοιχη τιμή:

counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
for key in counts:
    print(key, counts[key])

Δείτε τί παράγεται στην έξοδο:

jan 100
chuck 1
annie 42

Και πάλι, τα κλειδιά δεν είναι ταξινομημένα.

Μπορούμε να χρησιμοποιήσουμε αυτό το μοτίβο για να εφαρμόσουμε τα διάφορα ιδιώματα βρόχου που περιγράψαμε νωρίτερα. Για παράδειγμα, αν θέλαμε να βρούμε όλες τις εγγραφές σε ένα λεξικό με τιμή πάνω από δέκα, θα μπορούσαμε να γράψουμε τον ακόλουθο κώδικα:

counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
for key in counts:
    if counts[key] > 10 :
        print(key, counts[key])

Ο βρόχος for επαναλαμβάνεται μέσω των κλειδιών του λεξικού, επομένως πρέπει να χρησιμοποιήσουμε τον τελεστή ευρετηρίου για να ανακτήσουμε την αντίστοιχη τιμή για κάθε κλειδί. Δείτε πώς φαίνεται η έξοδος:

jan 100
annie 42

Βλέπουμε μόνο τις εγγραφές με τιμή πάνω από 10.

Εάν θέλετε να εκτυπώσετε τα κλειδιά με αλφαβητική σειρά, πρέπει να κάνετε πρώτα μια λίστα με τα κλειδιά του λεξικού, χρησιμοποιώντας τη μέθοδο keys, που είναι διαθέσιμη για αντικείμενα λεξικού και, στη συνέχεια, να ταξινομήστε τη λίστα και να τη διασχίσετε, αναζητώντας κάθε κλειδί και να εκτυπώσετε ζεύγη κλειδιών-τιμών ταξινομημένα, ως εξής:

counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
lst = list(counts.keys())
print(lst)
lst.sort()
for key in lst:
    print(key, counts[key])

Δείτε πώς φαίνεται η έξοδος:

['jan', 'chuck', 'annie']
annie 42
chuck 1
jan 100

Πρώτα βλέπετε τη λίστα των κλειδιών σε μη ταξινομημένη σειρά, όπως τη λαμβάνουμε από τη μέθοδο keys. Στη συνέχεια, βλέπουμε τα ζεύγη κλειδιού-τιμής ταξινομημένα από τον βρόχο for.

Προχωρημένη ανάλυση κειμένου

Στο παραπάνω παράδειγμα, χρησιμοποιώντας το αρχείο romeo.txt, απλοποιήσαμε το αρχείο, αφαιρώντας όλα τα σημεία στίξης με το χέρι. Το πραγματικό κείμενο έχει πολλά σημεία στίξης, όπως φαίνεται παρακάτω.

But, soft! what light through yonder window breaks?
It is the east, and Juliet is the sun.
Arise, fair sun, and kill the envious moon,
Who is already sick and pale with grief,

Μιας και η συνάρτηση split, της Python, αναζητά κενά και αντιμετωπίζει τις λέξεις ως διακριτικά που χωρίζονται με κενά, θα αντιμετωπίζαμε τις λέξεις “soft!” και “soft” ως διαφορετικές λέξεις και θα δημιουργούνταν μια ξεχωριστή καταχώρηση λεξικού για κάθε μια από αυτές τις λέξεις.

Επίσης, δεδομένου ότι το αρχείο έχει κεφαλαία και πεζά γράμματα, θα αντιμετωπίζαμε το “who” και το “Who” ως διαφορετικές λέξεις, με διαφορετικές μετρήσεις.

Μπορούμε να λύσουμε και τα δύο αυτά προβλήματα χρησιμοποιώντας τις μεθόδους συμβολοσειράς lower, punctuation και translate. Η translate είναι η πιο λεπτή από τις μεθόδους. Ακολουθεί η τεκμηρίωση για την translate:

line.translate(str.maketrans(fromstr, tostr, deletestr))

Αντικαταστήστε τους χαρακτήρες που περιλαμβάνονται στο fromstr με τον αντίστοιχο χαρακτήρα του tostr και διαγράψτε όλους τους χαρακτήρες που βρίσκονται στο deletestr. Το fromstr και το tostr μπορεί να είναι κενές συμβολοσειρές και η παράμετρος deletestr μπορεί να παραλειφθεί.

Δεν θα καθορίσουμε το tostr, αλλά θα χρησιμοποιήσουμε την παράμετρο deletestr για να διαγράψουμε όλα τα σημεία στίξης. Ακόμη, θα αφήσουμε την Python να μας πει τη λίστα των χαρακτήρων, που θεωρεί “σημεία στίξης”:

>>> import string
>>> string.punctuation
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

Οι παράμετροι της translate ήταν διαφορετικές στην Python 2.0.

Κάνουμε τις ακόλουθες τροποποιήσεις στο πρόγραμμά μας:

import string

fname = input('Εισαγάγετε το όνομα του αρχείου: ')
try:
    fhand = open(fname)
except:
    print('Δεν είναι δυνατό το άνοιγμα του αρχείου:', fname)
    exit()

πλήθη = dict()
for γραμμή in fhand:
    γραμμή = γραμμή.rstrip()
    γραμμή = γραμμή.translate(γραμμή.maketrans('', '', string.punctuation))
    γραμμή = γραμμή.lower()
    λέξεις = γραμμή.split()
    for λέξη in λέξεις:
        if λέξη not in πλήθη:
            πλήθη[λέξη] = 1
        else:
            πλήθη[λέξη] += 1

print(πλήθη)

# Code: http://www.py4e.com/code3/count2.py

Μέρος της εκμάθησης της “Τέχνης της Python” ή της “Python-ικής Σκέψης” είναι η συνειδητοποίηση ότι η Python έχει συχνά ενσωματωμένες δυνατότητες για πολλά κοινά προβλήματα ανάλυσης δεδομένων. Με τον καιρό, θα δείτε αρκετά παράδειγματα κώδικα και θα διαβάσετε αρκετά την τεκμηρίωση, για να ξέρετε πού να ψάξετε, προκειμένου να ελέγξετε εάν κάποιος έχει ήδη γράψει κάτι που κάνει τη δουλειά σας πολύ πιο εύκολη.

Η παρακάτω είναι μια συντομευμένη έκδοση της εξόδου:

Εισαγάγετε το όνομα του αρχείου: romeo-full.txt
{'swearst': 1, 'all': 6, 'afeard': 1, 'leave': 2, 'these': 2,
'kinsmen': 2, 'what': 11, 'thinkst': 1, 'love': 24, 'cloak': 1,
a': 24, 'orchard': 2, 'light': 5, 'lovers': 2, 'romeo': 40,
'maiden': 1, 'whiteupturned': 1, 'juliet': 32, 'gentleman': 1,
'it': 22, 'leans': 1, 'canst': 1, 'having': 1, ...}

Η αναζήτηση σε αυτήν της εξόδου εξακολουθεί να είναι δυσκίνητη. Μπορούμε όμως να χρησιμοποιήσουμε την Python για να μας δώσει ακριβώς αυτό που ψάχνουμε, αλλά για να το κάνουμε αυτό, πρέπει να μάθουμε για τις πλειάδες της Python. Θα συνεχίσουμε αυτό το παράδειγμα μόλις μάθουμε για τις πλειάδες.

Εκσφαλμάτωση

Καθώς εργάζεστε με μεγαλύτερα σύνολα δεδομένων, ο εντοπισμός σφαλμάτων μπορεί να γίνει δύσκολος με την εκτύπωση και τον έλεγχο των δεδομένων με το χειροκίνητα. Ακολουθούν ορισμένες προτάσεις για τον εντοπισμό σφαλμάτων σε μεγάλα συνόλα δεδομένων:

Μειώστε την είσοδο

Εάν είναι δυνατόν, μειώστε το μέγεθος του συνόλου δεδομένων. Για παράδειγμα, εάν το πρόγραμμα διαβάζει ένα αρχείο κειμένου, ξεκινήστε μόνο με τις πρώτες 10 γραμμές ή με ένα μικρότερο παράδειγμα, που μπορεί να βρείτε. Μπορείτε είτε να επεξεργαστείτε τα ίδια τα αρχεία, είτε (καλύτερα) να τροποποιήσετε το πρόγραμμα ώστε να διαβάζει μόνο τις πρώτες n γραμμές.

Εάν υπάρχει σφάλμα, μπορείτε να μειώσετε το n στη μικρότερη τιμή που εμφανίζει το σφάλμα και, στη συνέχεια, να το αυξήσετε σταδιακά, καθώς βρίσκετε και διορθώνετε τα σφάλματα.

Ελέγξτε τις περιλήψεις και τους τύπους

Αντί να εκτυπώσετε και να ελέγξετε ολόκληρο το σύνολο δεδομένων, σκεφτείτε να εκτυπώσετε περιλήψεις των δεδομένων: για παράδειγμα, τον αριθμό των στοιχείων σε ένα λεξικό ή το σύνολο μιας λίστας αριθμών.

Μια κοινή αιτία σφαλμάτων χρόνου εκτέλεσης είναι μια τιμή που δεν είναι του σωστού τύπου. Για τον εντοπισμό σφαλμάτων αυτού του είδους, συχνά αρκεί να εκτυπώσετε τον τύπο μιας τιμής.

Γράψτε αυτοελέγχους
Μερικές φορές μπορείτε να γράψετε κώδικα για να ελέγξετε αυτόματα για σφάλματα. Για παράδειγμα, εάν υπολογίζετε τον μέσο όρο μιας λίστας αριθμών, μπορείτε να ελέγξετε ότι το αποτέλεσμα δεν είναι μεγαλύτερο από το μέγιστο στοιχείο στη λίστα ή μικρότερο από το ελάχιστο. Αυτό ονομάζεται “έλεγχος λογικής”, επειδή εντοπίζει αποτελέσματα που είναι “παράλογα”.

Ένα άλλο είδος ελέγχου συγκρίνει τα αποτελέσματα δύο διαφορετικών
υπολογισμών για να δει αν είναι συνεπή. Αυτό ονομάζεται «έλεγχος
συνέπειας».
Εκτυπώστε όμορφα το αποτέλεσμα (pprint)
Η μορφοποίηση της εξόδου εντοπισμού σφαλμάτων μπορεί να διευκολύνει τον εντοπισμό ενός σφάλματος.

Και πάλι, ο χρόνος που αφιερώνετε στην μελέτη της κατασκευής του σκελετού του προγράμματος μπορεί να μειώσει τον χρόνο που αφιερώνετε στην αποσφαλμάτωση.

Γλωσσάριο

αναζήτηση
Μια λειτουργία λεξικού που παίρνει ένα κλειδί και βρίσκει την αντίστοιχη τιμή.
εμφωλευμένοι βρόχοι
Όταν υπάρχουν ένας ή περισσότεροι βρόχοι ο ένας “μέσα” στον άλλο βρόχο. Ο εσωτερικός βρόχος εκτελείτε μέχρι να ολοκληρωθεί σε κάθε εκτέλεση του εξωτερικού βρόχου.
ζεύγος κλειδιού-τιμής
Η αναπαράσταση της αντιστοίχισης ενός κλειδιού σε μια τιμή.
ιστόγραμμα
Ένα σύνολο μετρητών.
κλειδί
Ένα αντικείμενο που εμφανίζεται σε ένα λεξικό ως το πρώτο μέρος ενός ζεύγους κλειδιού-τιμής.
λεξικό
Μια αντιστοίχιση από ένα σύνολο κλειδιών στις αντίστοιχες τιμές τους.
πίνακας κατακερματισμού - hashtable
Ο αλγόριθμος που χρησιμοποιείται για την υλοποίηση λεξικών στην Python.
στοιχείο
Ένα άλλο όνομα για ένα ζεύγος κλειδιού-τιμής.
συνάρτηση κατακερματισμού - hash function
Μια συνάρτηση που χρησιμοποιείται από έναν πίνακα κατακερματισμού για τον υπολογισμό της θέσης ενός κλειδιού.
τιμή
Ένα αντικείμενο που εμφανίζεται σε ένα λεξικό ως το δεύτερο μέρος ενός ζεύγους κλειδιού-τιμής. Αυτό είναι πιο συγκεκριμένο από την προηγούμενη χρήση της λέξης “τιμή”.
υλοποίηση
Ένας τρόπος εκτέλεσης ενός υπολογισμού.

Ασκήσεις

Άσκηση 2: Γράψτε ένα πρόγραμμα που ταξινομεί κάθε μήνυμα αλληλογραφίας με βάση την ημέρα της εβδομάδας που ολοκληρώθηκε η παράδοση. Για να το κάνετε αυτό, αναζητήστε γραμμές που ξεκινούν με “From”, στη συνέχεια αναζητήστε την τρίτη λέξη και καταμετρήστε την κάθε μία από τις ημέρες της εβδομάδας. Στο τέλος του προγράμματος εκτυπώστε τα περιεχόμενα του λεξικού σας (η σειρά δεν έχει σημασία).

Δείγμα Γραμμής:

From [email protected] Sat Jan  5 09:14:16 2008

Δείγμα Εκτέλεσης:

python dow.py
Εισαγάγετε το όνομα του αρχείου: mbox-short.txt
{'Fri': 20, 'Thu': 6, 'Sat': 1}

Άσκηση 3: Γράψτε ένα πρόγραμμα για να διαβάσετε ένα αρχείο καταγραφής αλληλογραφίας, δημιουργήστε ένα ιστόγραμμα χρησιμοποιώντας ένα λεξικό για να μετρήσετε πόσα μηνύματα έχουν προέλθει από κάθε διεύθυνση email και εκτυπώστε το λεξικό.

Εισαγάγετε το όνομα του αρχείου: mbox-short.txt
{'[email protected]': 1, '[email protected]': 3,
'[email protected]': 5, '[email protected]': 1,
'[email protected]': 2, '[email protected]': 3,
'[email protected]': 4, '[email protected]': 1,
'[email protected]': 4, '[email protected]': 2,
'[email protected]': 1}

Άσκηση 4: Προσθέστε κώδικα στο προηγούμενο πρόγραμμα για να καταλάβετε ποιος έχει τα περισσότερα μηνύματα στο αρχείο. Αφού διαβάσετε όλα τα δεδομένα και το δημιουργήσετε το λεξικό, κοιτάξτε στο λεξικό χρησιμοποιώντας έναν βρόχο μέγιστου (βλ. Κεφάλαιο 5: Βρόχος μέγιστου και ελάχιστου), για να βρείτε ποιος έχει τα περισσότερα μηνύματα και να εκτυπώσετε πόσα μηνύματα έχει το άτομο αυτό.

Εισαγάγετε το όνομα του αρχείου: mbox-short.txt
[email protected] 5

Εισαγάγετε το όνομα του αρχείου: mbox.txt
[email protected] 195

Άσκηση 5: Αυτό το πρόγραμμα καταγράφει το όνομα τομέα (αντί για τη διεύθυνση) από όπου στάλθηκε το μήνυμα, αντί από ποιον προήλθε το μήνυμα (δηλαδή ολόκληρη η διεύθυνση email) και μετρά το πλήθος των μηνυμάτων από τον τομέα αυτό. Στο τέλος του προγράμματος, εκτυπώστε τα περιεχόμενα του λεξικού σας.

python schoolcount.py
Εισαγάγετε το όνομα του αρχείου: mbox-short.txt
{'media.berkeley.edu': 4, 'uct.ac.za': 6, 'umich.edu': 7,
'gmail.com': 1, 'caret.cam.ac.uk': 1, 'iupui.edu': 8}

Αν εντοπίσετε κάποιο λάθος σε αυτό το βιβλίο μην διστάσετε να μου στείλετε τη διόρθωση στο Github.