Τα λεξικά είναι ένα εργαλείο αρκετά ισχυρότερο από όσα έχουμε δει μέχρι τώρα στο οπλοστάσιο της Python. Σήμερα θα δούμε κάποιες ακόμα χρήσιμες μεθόδους και λειτουργίες των λεξικών. Με την βοήθεια τους θα μάθουμε στη συνέχεια πώς να δημιουργούμε δομές δεδομένων, ώστε να αναπαριστούμε αντικείμενα της καθημερινότητάς μας.
Προτάσεις συνεργασίας
Τα νέα άρθρα του PCsteps
Γίνε VIP μέλος στο PCSteps
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.
Μέθοδοι keys(), values(), items()
Συχνά θα χρειαστεί να πάρουμε από ένα λεξικό τιμές με τη μορφή λίστας, είτε αυτές αφορούν κλειδιά του λεξικού, είτε τιμές του, είτε και τα δύο μαζί. Υπάρχουν τρεις μέθοδοι με τις οποίες μπορεί να γίνει αυτό: η keys(), η values(), και η items().
Οι τιμές που επιστρέφονται από τις μεθόδους αυτές δεν είναι πραγματικές λίστες· δεν μπορούμε να τις τροποποιήσουμε ή να χρησιμοποιήσουμε μεθόδους όπως η append().
Μπορούμε όμως να τις αξιοποιήσουμε σε βρόχους for. Ας δούμε πώς λειτουργούν οι μέθοδοι αυτοί στον IDLE.
Εδώ περνάμε από κάθε τιμή του my_dict με ένα for. Το ίδιο βέβαια μπορούμε να κάνουμε και για κάθε κλειδί του my_dict ή για κάθε ζεύγος κλειδιού-τιμής:
Βλέπουμε ότι με τις keys(), values(), και items(), συνδυασμένες με ένα for, περνάμε αντίστοιχα από όλα τα κλειδιά, τις τιμές, ή τα ζεύγη κλειδιού-τιμής ενός λεξικού. Οι τιμές που επιστρέφει η items(), ειδικότερα, είναι πλειάδες με το κλειδί και την τιμή.
Οι μέθοδοι αυτοί δεν μας επιστρέφουν λίστα, αλλά μια δομή παρόμοια με τη λίστα. Αν θέλουμε τα δεδομένα σε πραγματική λίστα, χρησιμοποιούμε τη list(). Αυτή θα πάρει την return value των keys(), values(), ή items() για να μας την επιστρέψει ως λίστα:
Μπορούμε, ακόμη, να χρησιμοποιήσουμε πολλαπλή ανάθεση σε ένα for για να περάσουμε το κλειδί και την τιμή σε διαφορετικές μεταβλητές:
Εύρεση κλειδιού/τιμής σε λεξικό
Θυμόμαστε από τα προηγούμενα μαθήματα ότι με τα in και not ελέγχουμε αν μια συγκεκριμένη τιμή υπάρχει σε μια λίστα.
Μπορούμε να χρησιμοποιήσουμε τους τελεστές αυτούς και σε ένα λεξικό, για να βρούμε αν περιλαμβάνει το κλειδί ή την τιμή που μας ενδιαφέρει.
Η έκφραση:
'name' in my_dict
…είναι ουσιαστικά συντόμευση της:
'name' in my_dict.keys()
Επομένως, μπορούμε να βρούμε αν μια τιμή αποτελεί κλειδί του λεξικού μας, χρησιμοποιώντας το in (ή not in) και το όνομα του λεξικού. Με άλλα λόγια παραλείπουμε δηλαδή το “.keys()”.
Η μέθοδος get()
Το να πρέπει να ελέγχουμε αν ένα κλειδί υφίσταται στο λεξικό και έπειτα να ανακτούμε την αντίστοιχη τιμή του γίνεται κουραστική διαδικασία.
Ευτυχώς τα λεξικά μας προσφέρουν – προς διευκόλυνσή μας – τη μέθοδο get(). Αυτή λαμβάνει δύο ορίσματα: το κλειδί της τιμής που θα ανακτήσουμε, καθώς και μια εφεδρική τιμή, αν το κλειδί αυτό δεν υπάρχει.
Πάμε να δούμε την get() στην πράξη.
Μιας και δεν υπάρχει κλειδί ‘apples' στο λεξικό “food”, επιστρέφεται από την get() η default τιμή 0. Χωρίς την get(), ο κώδικας θα προκαλούσε error:
Η μέθοδος setdefault()
Συχνά θα χρειαστεί να θέσουμε μια τιμή για κάποιο κλειδί του λεξικού μας, μόνο υπό την προϋπόθεση ότι το κλειδί αυτό δεν έχει ήδη τιμή. Ένας τρόπος να γίνει αυτό θα ήταν ο ακόλουθος:
Η setdefault() μας προσφέρει μια εναλλακτική επιλογή σε μία μόνο γραμμή κώδικα.
Το πρώτο όρισμα της μεθόδου είναι το κλειδί που θα ελέγξουμε, και το δεύτερο η τιμή που θα του αποδώσουμε αν το κλειδί δεν υπάρχει. Σε περίπτωση που το κλειδί υπάρχει, η setdefault() θα επιστρέψει την αντίστοιχη τιμή.
Την πρώτη φορά που καλείται η setdefault(), το λεξικό της my_dict τροποποιείται και περιλαμβάνει πλέον το νέο ζεύγος κλειδιού-τιμής. Η μέθοδος επιστρέφει την τιμή 1.8, καθώς αυτή είναι πλέον η τιμή για το κλειδί ‘height'.
Όταν ξανακαλέσουμε την setdefault() με το ίδιο κλειδί αλλά διαφορετική τιμή:
my_dict.setdefault('height', 1.50)
…η τιμή του κλειδιού δεν αλλάζει σε 1.50, καθώς υπάρχει ήδη κλειδί με το όνομα ‘height'.
H μέθοδος setdefault() είναι ένας γρήγορος τρόπος να διασφαλίσουμε αν ένα κλειδί υπάρχει στο λεξικό.
Ας γράψουμε ένα πρόγραμμα που μετράει το πλήθος των εμφανίσεων κάθε γράμματος σε ένα string:
Το πρόγραμμα περνάει από κάθε χαρακτήρα του string “message”, μετρώντας πόσο συχνά εμφανίζεται κάθε χαρακτήρας.
Με τη setdefault() είμαστε σίγουροι ότι το κλειδί υπάρχει στο λεξικό count (με εφεδρική τιμή το 0), ώστε το πρόγραμμα να μην παράγει KeyError όταν εκτελείται η εντολή:
count[character] = count[character] + 1
Το αποτέλεσμα της εκτύπωσης είναι το εξής:
Pretty printing
Αν εισάγουμε το module pprint στο πρόγραμμά μας, θα έχουμε στη διάθεσή μας τις συναρτήσεις pprint() και pformat(), με τις οποίες μπορούμε να κάνουμε “pretty print” τις τιμές του λεξικού.
Με αυτήν την εκτύπωση θα έχουμε πιο καθαρή απεικόνιση των αντικειμένων ενός λεξικού από ό,τι με την print().
Ας εφαρμόσουμε την pprint() στο προηγούμενο πρόγραμμα:
Αυτή τη φορά εκτελώντας το πρόγραμμα, το αποτέλεσμα της εκτύπωσης φαίνεται καλύτερα, με τα κλειδιά ταξινομημένα.
Η συνάρτηση pprint.pprint() θα μας φανεί ιδιαίτερα χρήσιμη, όταν το ίδιο το λεξικό περιέχει εμφωλευμένες λίστες ή λεξικά.
Αν δεν μας ενδιαφέρει να το δούμε στην οθόνη, αλλά θέλουμε να ανακτήσουμε ως string το κείμενο που εκτυπώνει η pprint.pprint(), χρησιμοποιούμε την pprint.pformat().
Οι δύο αυτές εκφράσεις, δηλαδή, θεωρούνται ισοδύναμες:
pprint.pprint(count) print(pprint.pformat(count))
Δομές δεδομένων – Aναπαράσταση αντικειμένων
Ακόμα και πριν την έλευση του υπολογιστή, μπορούσαμε να προσομοιώσουμε ένα παιχνίδι σκάκι στο χαρτί, χωρίς να έχουμε ταμπλό.
Για να γίνει αυτό, έπρεπε να σκεφτούμε έναν τρόπο για να περιγράψουμε με σαφήνεια την κατάσταση του ταμπλό και τις κινήσεις.
Με τον αλγεβρικό συμβολισμό για το σκάκι, που σίγουρα θα έχουμε προσέξει στις περισσότερες σκακιέρες, τα κουτάκια του ταμπλό ταυτοποιούνται από συντεταγμένες. Οι συντεταγμένες αυτές παράγονται από τον συνδυασμό γραμμάτων και αριθμών.
Για να ολοκληρωθεί η αναπαράσταση του παιχνιδιού, το μόνο που μένει είναι να χρησιμοποιήσουμε διαφορετικά γράμματα για το κάθε πιόνι: K για τον βασιλιά (king), Q για τη βασίλισσα (queen), κλπ.
Με ανάλογο τρόπο μπορεί και ο υπολογιστής να αναπαραστήσει ένα παιχνίδι σκάκι, χωρίς να υπάρχει πραγματική σκακιέρα. Εν ολίγοις, μοντελοποιεί δεδομένα για να κάνει μια προσομοίωση της σκακιέρας, και με τον αντίστοιχο κώδικα μας επιτρέπει να δουλέψουμε με το μοντέλο αυτό.
Κάπου εδώ έρχονται οι λίστες και τα λεξικά, αφού μπορούμε να τα χρησιμοποιήσουμε για να μοντελοποιήσουμε πραγματικά αντικείμενα, όπως μια σκακιέρα.
Παράδειγμα προγράμματος
Επειδή το σκάκι είναι αρκετά πολύπλοκο, θα προσπαθήσουμε να κατασκευάσουμε μια δομή δεδομένων για ένα πολύ πιο απλό παιχνίδι: τη γνωστή σε όλους μας τρίλιζα.
Για όσους παίζατε ή παίζετε ακόμα τρίλιζα, θα θυμάστε (για τα 5 δευτερόλεπτα που διαρκούσε) τους κανόνες: σε ένα πρόχειρο ταμπλό σχεδιασμένο σαν hashtag (#), παίζουν δύο παίκτες με τη σειρά, ο ένας μετά τον άλλον.
Στα εννέα κενά κελιά που χαράσσουν οι γραμμές του ταμπλό, οι παίκτες αφήνουν από ένα σημάδι σε κάθε τους κίνηση. Όποιος καταφέρει να τοποθετήσει κάθετα, οριζόντια, ή διαγώνια, τρία σημάδια στη σειρά, είναι ο νικητής.
Αρχικά, ας δούμε πώς αναπαριστούμε τι περιέχει κάθε κελί. Μπορούμε να χρησιμοποιήσουμε ένα string για κάθε περίπτωση:
‘X' για το σημάδι του πρώτου παίκτη, ‘O' για του δεύτερου, και ‘ ‘, αν το κελί είναι ακόμα κενό. Επίσης, θα πρέπει να αποθηκεύσουμε εννέα string, ένα για κάθε διαθέσιμο κελί του ταμπλό.
Για την εργασία αυτήν μπορούμε να χρησιμοποιήσουμε λεξικό. Ως κλειδιά θα μπουν εννέα διαφορετικές λέξεις, για να χαρακτηρίσουμε ξεχωριστά κάθε κελί. Ας επιλέξουμε τα string ‘top-left' για το πάνω αριστερά κελί, ‘mid-left' για το μεσαίο αριστερά, ‘low-left για το κάτω αριστερά, κοκ.
Το λεξικό μας λοιπόν είναι μια δομή που αναπαριστά το ταμπλό της τρίλιζας. Το αποθηκεύουμε στη μεταβλητή board.
board = {'top-left': ' ', 'top-mid': ' ', 'top-right': ' ', 'mid-left': ' ', 'mid-mid': ' ', 'mid-right': ' ', 'low-left': ' ', 'low-mid': ' ', 'low-right': ' '}
Εφόσον η τιμή για κάθε κλειδί του board είναι ένα string μήκους ενός χαρακτήρα (κενού), το λεξικό για την ώρα αναπαριστά ένα τελείως κενό ταμπλό.
Έστω ότι ο παίκτης X παίζει πρώτος και διαλέγει (κλασικά) το μεσαίο κελί. Το λεξικό μας θα τροποποιηθεί:
board = {'top-left': ' ', 'top-mid': ' ', 'top-right': ' ', 'mid-left': ' ', 'mid-mid': 'X', 'mid-right': ' ', 'low-left': ' ', 'low-mid': ' ', 'low-right': ' '}
Πλέον το ταμπλό που αναπαριστά το λεξικό έχει διαμορφωθεί ως εξής:
Ας υποθέσουμε τώρα ότι ο παίκτης Χ είναι άσχετος, και ο παίκτης O κατάφερε να κερδίσει δημιουργώντας τριάδα στην τελευταία σειρά. Το λεξικό της board θα έχει διαμορφωθεί κάπως έτσι:
board = {'top-left': 'X', 'top-mid': 'X', 'top-right': ' ', 'mid-left': ' ', 'mid-mid': 'X', 'mid-right': ' ', 'low-left': 'O', 'low-mid': 'O', 'low-right': 'O'}
Η δομή δεδομένων που αναπαριστά η board θα είναι η εξής:
Όπως είναι φυσικό, ο παίκτης βλέπει μόνο ό,τι τυπώνεται στην οθόνη, όχι τα ίδια τα περιεχόμενα των μεταβλητών. Οπότε ας φτιάξουμε μια συνάρτηση για να γίνεται η εκτύπωση του ταμπλό της board στην οθόνη:
Εκτελώντας το πρόγραμμά μας με ό,τι έχουμε γράψει ως τώρα, εκτυπώνεται ένα κενό ταμπλό τρίλιζας.
Η συνάρτηση print_board() μπορεί να χειριστεί οποιαδήποτε δεδομένα τρίλιζας της περάσουμε. Δοκιμάζουμε να αλλάξουμε τον κώδικα με το παρακάτω:
Τώρα με την εκτέλεση του προγράμματος, η εκτύπωση έχει αλλάξει:
Μένει να γράψουμε κώδικα που να επιτρέπει την εισαγωγή κινήσεων από τους παίκτες. Μπορούμε να προσθέσουμε στο πρόγραμμά μας το παρακάτω, αν θέλουμε το παιχνίδι να διαρκεί εννιά γύρους:
Ο νέος κώδικας τυπώνει το ταμπλό στην έναρξη κάθε γύρου. Έπειτα, παίρνει ως είσοδο την κίνηση του χρήστη που παίζει στον συγκεκριμένο γύρο, και ανανεώνει το ταμπλό αντίστοιχα. Τέλος αλλάζει τον τρέχοντα παίκτη (από ‘X' σε ‘O' και αντίστροφα), και προχωρά στον επόμενο γύρο.
Εκτελώντας το πρόγραμμα, θα δούμε κάτι τέτοιο:
Δημιουργήσαμε λοιπόν μια δομή δεδομένων που αποτελεί αρκετά καλή προσομοίωση της τρίλιζας.
Ίσως το πρόγραμμα να μην είναι πλήρες ακόμα. Για παράδειγμα, δεν ελέγχει αν αναδείχθηκε νικητής κάποιος από τους δύο παίκτες. Είδαμε όμως πώς μπορούμε να χειριστούμε μια δομή δεδομένων σε ένα πρόγραμμα.
Στο επόμενο μάθημα για τον προγραμματισμό Python
Έχουμε – επιτέλους – τελειώσει με το βασικό συντακτικό της Python.
Την ερχόμενη εβδομάδα μπορούμε να ασχοληθούμε σε βάθος με τα string, πώς να τα χειριστούμε σε κάθε μορφή, πώς τα εκμεταλλευόμαστε για να εκτελέσουμε εργασίες, χρήσιμες συναρτήσεις τους, και πολλά άλλα.
Σας άρεσε το σημερινό μάθημα για τον προγραμματισμό Python?
Για οποιαδήποτε ιδέα, πρόταση, ή απορία σχετικά με το σημερινό ή τα προηγούμενα μαθήματα, αφήστε σχόλιο.
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.