Στο σημερινό μάθημα για τον προγραμματισμό Python, θα εξετάσουμε ακόμα έναν νέο τύπο δεδομένων, τη λίστα. Πρόκειται για μια δομή που θα μας φανεί χρήσιμη σε αμέτρητες περιπτώσεις, μειώνοντας τον όγκο των προγραμμάτων που γράφουμε. Θα ρίξουμε, επίσης, μια ματιά σε μερικές χρήσιμες μεθόδους που μπορούμε να αξιοποιήσουμε με τις λίστες μας.
Προτάσεις συνεργασίας
Τα νέα άρθρα του PCsteps
Γίνε VIP μέλος στο PCSteps
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.
Πρόγραμμα προηγούμενου μαθήματος
Πριν συνεχίσουμε, ας δούμε μια υλοποίηση του προγράμματος που συζητήσαμε στο τελευταίο μάθημα.
#collatz sequence program def collatz(number): if number % 2 == 0: print(number // 2) return(number // 2) elif number % 2 == 1: print(3 * number + 1) return(3 * number + 1) print('Please enter an integer') try: num = int(input()) while num != 1: except ValueError: print('That was not an integer.')
Αρχικά, δημιουργούμε μια νέα συνάρτηση, την collatz(). Αυτή δέχεται έναν αριθμό και ελέγχει το υπόλοιπο της διαίρεσής του με το 2 (σύμβολο %). Αν είναι 0, έχουμε ζυγό αριθμό, ενώ αν είναι 1, μονό. Σε κάθε περίπτωση, εκτελούμε τον ανάλογο υπολογισμό, όπως μας ζητήθηκε από την εκφώνηση.
Ταυτόχρονα, εκτυπώνουμε το αποτέλεσμα με το print(), προκειμένου να δημιουργηθεί σιγά-σιγά η ακολουθία στην οθόνη. Επίσης, το επιστρέφουμε με το return, για να χρησιμοποιηθεί στη συνέχεια εκτέλεσης του προγράμματος.
Κατόπιν, αφού ζητήσουμε από τον χρήστη να πληκτρολογήσει έναν ακέραιο αριθμό, τον περνάμε στην μεταβλητή “num”. Χρησιμοποιούμε, όπως έχουμε μάθει, την int(), προκειμένου να μετατρέψουμε σε ακέραιο το string που εισήγαγε ο χρήστης.
Τέλος, δημιουργούμε έναν απλό βρόχο. Μέχρι να φτάσουμε στη μονάδα, που είναι και το ζητούμενο της ακολουθίας, ο βρόχος επαναλαμβάνεται: Καλούμε τη συνάρτηση που φτιάξαμε, με όρισμα την μεταβλητή “num”, και θέτουμε πάλι στη “num” το αποτέλεσμα που επιστρέφεται.
Aρχικά λοιπόν, η συνάρτηση τσεκάρει και κάνει υπολογισμούς με βάση τον αριθμό που μας δόθηκε. Σε κάθε επόμενη επανάληψη, θα εκτελεί τις ίδιες πράξεις με τον αριθμό που η ίδια επέστρεψε στην προηγούμενη.
Προαιρετικά, χρησιμοποιήσαμε και ένα try – except για να ελέγχουμε την περίπτωση που ο χρήστης εισήγαγε κάτι άλλο πέραν ενός ακεραίου.
Λίστες
Οι λίστες αποτελούν ένα είδος ακολουθίας, που είναι μια από τις βασικές δομές μιας γλώσσας προγραμματισμού. Η λίστα μπορεί να περιέχει πολλαπλές τιμές (και όχι απαραίτητα μόνο μία) σε μια συνεχόμενη σειρά. Ο όρος “λίστα” αναφέρεται στο ίδιο το αντικείμενο της λίστας, και όχι στις τιμές που περιέχει.
Για παράδειγμα, το παρακάτω θα μπορούσε να είναι μια λίστα:
['paok', 'olympiakos', 'aek', 'iraklis']
Όπως ένα string, που θεωρείται κι αυτό ακολουθία, αρχίζει και τελειώνει με εισαγωγικά (”), έτσι και η λίστα ξεκινάει και κλείνει με τις αγκύλες [ ]. Τα στοιχεία που βρίσκονται μέσα στη λίστα λέγονται αντικείμενα (items) και χωρίζονται μεταξύ τους με κόμμα.
Στη μεταβλητή “my_list” έχουμε αναθέσει μόνο μία τιμή: την τιμή της λίστας. Αυτή η τιμή με τη σειρά της περιέχει άλλες, που είναι τα αντικείμενα της λίστας.
Κατ' αντιστοιχία πάλι με τα string, το ανάλογο του κενού string (“”) είναι η κενή λίστα ([]), μια λίστα δηλαδή που δεν περιέχει τιμές.
Δείκτες – Πρόσβαση σε μεμονωμένες τιμές της λίστας
Έστω ότι έχουμε αποθηκευμένη τη λίστα του προηγούμενου παραδείγματος στη “my_list”. Γράφοντας “my_list[0]”, η Python καταλαβαίνει ότι αναφερόμαστε στο πρώτο αντικείμενο της λίστας, δηλαδή στο ‘paok'.
Ομοίως, αν γράψουμε “my_list[2]”, αποκτούμε πρόσβαση στο τρίτο αντικείμενο της λίστας, ‘iraklis'. Ο ακέραιος που βρίσκεται εντός των αγκύλων λέγεται δείκτης (index) και, χρησιμοποιώντας τον, μπορούμε να αναφερθούμε μεμονωμένα σε κάθε τιμή της λίστας.
Η αρίθμηση γίνεται όπως συνήθως στην πληροφορική, ξεκινώντας από το “0” και όχι από το “1”.
Σε περίπτωση που χρησιμοποιήσουμε δείκτη που υπερβαίνει το πλήθος των τιμών της λίστας μας, η Python θα μας τυπώσει IndexError.
Επίσης, ο δείκτης μπορεί να είναι μόνο ακέραιος. Αν χρησιμοποιήσουμε άλλον τύπο, πχ float, η Python θα μας τυπώσει TypeError.
Οι λίστες είναι δυνατόν να περιέχουν ως τιμές άλλες λίστες. Για να αποκτήσουμε πρόσβαση σε αυτές, χρησιμοποιούμε πολλαπλούς δείκτες.
Ο πρώτος δείκτης υποδεικνύει μια εσωτερική λίστα που βρίσκεται εντός της πρώτης, (my_list) ενώ ο δεύτερος τη συγκεκριμένη τιμή της λίστας που περιέχεται στην πρώτη.
Αν χρησιμοποιήσουμε έναν μόνο δείκτη, το πρόγραμμα θα τυπώσει ολόκληρη την εσωτερική λίστα που βρίσκεται στη θέση αυτή.
Αρνητικοί δείκτες
Προηγουμένως χρησιμοποιήσαμε δείκτες για να κινηθούμε από την πρώτη θέση μιας λίστας προς την τελευταία. Αντίστοιχα, μπορούμε να κινηθούμε και με την ανάποδη φορά με αρνητικό δείκτη.
Στην πρώτη περίπτωση, το “0” αφορά την πρώτη θέση της λίστας, το “1” τη δεύτερη, κοκ. Έτσι, με αρνητικούς δείκτες, το “-1” αναφέρεται στο τελευταίο στοιχείο της λίστας, το “-2” στο προτελευταίο, κλπ.
Αυτή την φορά η θέση του στοιχείου είναι στον ίδιο αριθμό με τον αρνητικό δείκτη. Δηλαδή το πρώτο στοιχείο από το τέλος στη θέση “-1”, το δεύτερο από το τέλος στη “-2”, και πάει λέγοντας.
Πρόσβαση σε υπολίστες
Όπως ο δείκτης μας επιστρέφει την τιμή μιας λίστας, το κομμάτι (slice) μας επιστρέφει περισσότερες τιμές της, υπό τη μορφή μιας νέας λίστας. Το slice μπαίνει κι αυτό μέσα σε αγκύλες, αλλά αντί για έναν ακέραιο αριθμό έχει δύο, χωρισμένους με άνω κάτω τελεία.
my_list[4] #list with index my_list[1:5] #list with slice
Μέσα στις αγκύλες, ο πρώτος αριθμός είναι το σημείο όπου αρχίζει το κομμάτι και ο δεύτερος εκεί που τελειώνει. Το στοιχείο της θέσης του δεύτερου αριθμού δεν περιλαμβάνεται στο slice. Εκείνο που επιστρέφεται είναι μια νέα υπολίστα.
Μπορούμε, για συντομία, να χρησιμοποιήσουμε μόνο έναν από τους δύο δείκτες του slice (ή και κανέναν).
Αν αφήσουμε κενό τον πρώτο, είναι σαν να ξεκινάμε από το 0, δηλαδή από την αρχή της λίστας. Αν αφήσουμε κενό τον δεύτερο, το slice θα φτάσει μέχρι το τέλος της λίστας.
Len() και λίστες
Γνωρίζουμε ήδη ότι με τη len() βρίσκουμε εύκολα το μήκος των χαρακτήρων ενός string. Με όμοιο τρόπο θα το χρησιμοποιήσουμε και για να μάθουμε πόσες τιμές είναι αποθηκευμένες σε μια λίστα. Γράφουμε στον IDLE:
Αλλαγή τιμών σε λίστες
Όπως έχουμε μάθει, για να αλλάξουμε την τιμή μιας μεταβλητής, κάνουμε μια τυπική δήλωση ανάθεσης. Για παράδειγμα:
var = 10
Στις λίστες όμως, μπορούμε να μεταβάλλουμε την τιμή μιας μεταβλητής χρησιμοποιώντας δείκτες. Γράφοντας για παράδειγμα…
my_list[3] = 'kwstetsos'
…το τέταρτο στοιχείο της “my_list” θα αντικατασταθεί από το string αυτό. Ας δούμε ένα παράδειγμα στον IDLE:
Συνένωση και αναπαραγωγή λιστών
Είχαμε δει πώς γίνεται η συνένωση (concatenation) και η αναπαραγωγή (replication) σε άλλους τύπους δεδομένων. Με τον τελεστή “+” μπορούμε να ενώσουμε δυο λίστες για τη δημιουργία μιας νέας, όπως ακριβώς συνέβαινε και με τα string.
Με τη βοήθεια του τελεστή “*” και ενός ακεραίου, μπορούμε να αναπαράγουμε τη λίστα, όσες φορές υποδεικνύει ο ακέραιος αυτός.
Διαγραφή τιμών με το del
Μια δήλωση del θα διαγράψει την τιμή μιας λίστας. Η τιμή που θα διαγραφεί εξαρτάται από τη θέση που ορίζει ο δείκτης. Μετά τη διαγραφή, όλες οι τιμές της λίστας θα μετακινηθούν προς τα πάνω κατά μία μονάδα.
Η δήλωση del μπορεί να αξιοποιηθεί και με μια απλή μεταβλητή, προκειμένου να τη διαγράψουμε.
Αν κάνουμε del μια μεταβλητή και προσπαθήσουμε έπειτα να την χρησιμοποιήσουμε, θα πάρουμε NameError. Αυτό σημαίνει ότι η μεταβλητή αυτή δεν υφίσταται πλέον.
Βέβαια στην πράξη, δεν θα χρειαστεί σχεδόν ποτέ να διαγράψουμε απλές μεταβλητές. Το del υπάρχει κυρίως για να κάνουμε delete τιμές από λίστες.
Χρήση λιστών στο πρόγραμμά μας
Όταν ξεκινάμε να γράψουμε ένα πρόγραμμα, είναι εύκολο να δημιουργούμε διάφορες μεταβλητές για ό,τι χρειαζόμαστε. Καλύτερη τακτική, όμως, είναι να συγκεντρώνουμε πολλές επαναλαμβανόμενες μεταβλητές σε μια λίστα.
Με αυτόν τον τρόπο, γλιτώνουμε πολλές επαναλαμβανόμενες εντολές που θα έπρεπε να γράψουμε μία – μία.
Ας πάρουμε το παράδειγμα ενός προγράμματος στο οποίο καταγράφουμε τα ονόματα μεγάλων Ελλήνων μόδιστρων. Χωρίς λίστα, θα έπρεπε να γράψουμε κάτι τέτοιο:
Ας γράψουμε τώρα μια νέα έκδοση του προγράμματος, που χρησιμοποιεί μία μόνο λίστα και μπορεί να αποθηκεύσει όσους μόδιστρους και αν εισάγει ο χρήστης.
Εκτελώντας το, έχουμε το ακόλουθο αποτέλεσμα:
Με τη λίστα τα δεδομένα μας είναι πλέον καλύτερα οργανωμένα, κι έτσι το πρόγραμμα πιο ευέλικτο στο να τα δέχεται και να τα επεξεργάζεται.
Λίστες και επαναληπτικοί βρόχοι
Παρατηρώντας ξανά τον βρόχο ενός for, καταλαβαίνουμε ότι η έκφραση:
for i in range(4) print(i)
…είναι αντίστοιχη της:
for i in [0, 1, 2, 3] print(i)
Ουσιαστικά δηλαδή, ο βρόχος περνάει τη μεταβλητή “i” μέσα από διαδοχικές τιμές μιας λίστας, της [0, 1, 2, 3].
Μια τυπική τακτική λοιπόν στην Python είναι να χρησιμοποιούμε σε for βρόχους το range(), μαζί με το μήκος μιας λίστας, αντί συγκεκριμένου αριθμού:
range(len(my_list))
Έτσι, είμαστε βέβαιοι ότι το range() θα περάσει από όλα τα αντικείμενα της λίστας μας, ασχέτως του πόσα είναι.
Τελεστές in και not in
Για να εξετάσουμε αν μια τιμή βρίσκεται σε μια λίστα ή να βεβαιωθούμε ότι δεν ανήκει σε αυτήν, έχουμε τους τελεστές in και not in.
Όπως οι υπόλοιποι τελεστές που έχουμε δει, χρησιμοποιούνται σε εκφράσεις και συνδέουν δύο τιμές: την τιμή που θα ψάξουν και την λίστα στην οποία θα ερευνήσουν. Οι εκφράσεις αυτές επιστρέφουν μια τιμή Boolean.
Πολλαπλή ανάθεση
Η πολλαπλή ανάθεση μας δίνει ένα σύντομο τρόπο να αναθέσουμε τιμές σε πολλές μεταβλητές ταυτόχρονα. Θα χρειαστούμε μάλιστα μόνο μια γραμμή κώδικα.
Αντί, λοιπόν, να γράψουμε…
…θα γράψουμε το κάτωθι.
Το πλήθος των μεταβλητών και το μήκος της λίστας πρέπει να είναι ακριβώς ίσα, αλλιώς η Python θα μας τυπώσει ValueError:
Μπορούμε ακόμα να χρησιμοποιήσουμε την πολλαπλή ανάθεση για να αντιμεταθέσουμε τις τιμές μεταξύ δύο μεταβλητών (swap).
Περισσότεροι τελεστές ανάθεσης
Όταν αναθέτουμε τιμή σε μια μεταβλητή, χρησιμοποιούμε συχνά την ίδια τη μεταβλητή και στο δεξί μέρος της δήλωσης. Για παράδειγμα:
Μπορούμε όμως να γράψουμε πιο σύντομα τις παραπάνω εντολές ως εξής:
Βλέπουμε, λοιπόν, ότι υπάρχουν και άλλοι τελεστές ανάθεσης, εκτός από τον “=” που γνωρίζουμε. Προκύπτουν από τον συνδυασμό του με έναν ακόμα τελεστή από τους +, -, *, /, %.
Η ανάθεση έτσι συνδυάζεται και με την πράξη που ορίζει ο τελεστής. Αν, για παράδειγμα, έχουμε την πράξη…
var += 10
…αυτό σημαίνει ότι προσθέτουμε τον αριθμό 10 στη μεταβλητή “var” και αναθέτουμε το αποτέλεσμα της πράξης στη “var”.
Μέθοδοι
Η μέθοδος είναι το ίδιο πράγμα με μία συνάρτηση, με τη διαφορά ότι χρησιμοποιείται από λίστες. Αν είχαμε μια μέθοδο index() για τη λίστα που είναι αποθηκευμένη στη my_list, θα την καλούσαμε ως εξής:
my_list.index('hello')
Μετά το όνομα της λίστας, και χωριζόμενο με τελεία, μπαίνει το όνομα της μεθόδου. Εντός της παρένθεσης μπαίνουν τα ορίσματα της μεθόδου, ένα ή περισσότερα, παρόμοια με μία συνάρτηση.
Εύρεση τιμής σε λίστα: index()
Οι λίστες έχουν έτοιμη από την Python τη μέθοδο index(). Περνώντας της μια τιμή, η index() θα επιστρέψει τον δείκτη της, αν η τιμή αυτή υπάρχει στη λίστα. Αν δεν υπάρχει, η Python θα παράγει ένα ValueError.
Μπορούμε να δοκιμάσουμε εισάγοντας στον IDLE τα παρακάτω:
Σε περίπτωση τιμής που υπάρχει στη λίστα πάνω από μία φορές, η index() θα επιστρέψει τον δείκτη της πρώτης της εμφάνισης. Στην ακόλουθη λίστα, δίνοντας ως όρισμα στην index() το “x”, θα πάρουμε ως αποτέλεσμα 0, και όχι 3.
Προσθήκη τιμής σε λίστα: append() και insert()
Με τις μεθόδους append() και insert() μπορούμε να προσθέσουμε νέες τιμές στη λίστα μας. Η append() εισάγει την τιμή στο τέλος της λίστας.
Με την insert() βάζουμε νέα τιμή σε όποια θέση της λίστας θέλουμε. Ως πρώτο όρισμα, η insert() παίρνει τον δείκτη για το σημείο όπου θα μπει η τιμή. Το δεύτερο όρισμα είναι η καινούργια τιμή:
Παρατηρώντας τις μεθόδους του παραδείγματος, διαπιστώνουμε ότι η εντολή συντάσσεται:
my_list.insert(1, 'aek')
Και όχι:
my_list = my_list.insert(1, 'aek')
Αυτό σημαίνει ότι η μέθοδος δεν επιστρέφει μια νέα τιμή της λίστας “my_list”. Αντίθετα, η λίστα τροποποιείται επί τόπου. Θα δούμε ότι η ιδιότητα αυτή είναι σημαντική, όταν εξετάσουμε τον διαχωρισμό μεταβλητών και μη μεταβλητών τύπων δεδομένων.
Διαγραφή τιμής από λίστα: remove()
Για να χρησιμοποιήσουμε την remove(), περνάμε ως όρισμα την τιμή που θέλουμε να διαγράψουμε από τη λίστα. Αν προσπαθήσουμε, όμως, να διαγράψουμε μια τιμή που δεν υπάρχει στη λίστα, θα πάρουμε ValueError.
Όπως και με την insert(), αν η τιμή υπάρχει περισσότερες από μία φορές στη λίστα, θα διαγράφει εκείνη που εμφανίζεται πρώτη στη λίστα.
Ταξινόμηση τιμών σε λίστα: sort()
Αν έχουμε αμιγή λίστα, που αποτελείται μόνο από string ή μόνο από αριθμούς, μπορούμε να την ταξινομήσουμε με τη μέθοδο sort(). Την καλούμε χωρίς ορίσματα:
Μπορούμε, τώρα, να την καλέσουμε βάζοντας ως όρισμα τη λέξη – κλειδί reverse, αφού θέσουμε στη reverse την τιμή True. Με αυτόν τον τρόπο, θα γίνει αντίστροφη ταξινόμηση της λίστας.
Όπως είπαμε και προηγουμένως, πρέπει η λίστα να περιέχει αποκλειστικά αριθμητικές τιμές ή αποκλειστικά string. Μια λίστα που έχει και από τα δύο, δεν είναι δυνατόν να ταξινομηθεί από την Python.
Επίσης, η ταξινόμηση που κάνει η sort() λαμβάνει υπόψη τον διαχωρισμό μικρών και κεφαλαίων γραμμάτων. Τα κεφαλαία προηγούνται εξ ολοκλήρου των μικρών. Έτσι, για παράδειγμα το μικρό ‘a' θα τοποθετηθεί μετά το κεφαλαίο ‘Z'.
Για να αποφύγουμε πιθανή κακοτοπιά, θα έπρεπε να μετατρέψουμε όλα τα γράμματα σε μικρά. Ωστόσο, με μια δήλωση key = str.lower ως όρισμα, η sort() θα συμπεριφερθεί σε όλα τα αντικείμενα της λίστας σαν να ήταν μικρά, χωρίς να τα μεταβάλλει.
Παράδειγμα προγράμματος
Στο τέταρτο μάθημα για την Python, μαθαίνοντας για τις return value, είχαμε δει ένα μικρό παράδειγμα με διαδοχικά elif.
Σήμερα θα κάνουμε μια απλή παραλλαγή του προγράμματος αυτού, που όμως καταδεικνύει πόσο η λίστα μειώνει τον κώδικα που πρέπει να γράψουμε.
Χρησιμοποιώντας μια λίστα, το πρόγραμμα μπορεί να γραφτεί σε τρεις μόλις σειρές.
Η μοναδική εντολή που παρέμεινε ίδια είναι η εισαγωγή του module random, που μας χρειάζεται για να έχουμε πρόσβαση στη συνάρτηση randint().
Τα διαδοχικά return των elif έχουν αντικατασταθεί από μία λίστα, τη “messages”, η οποία περιέχει όλα τα πιθανά string που θέλουμε να εκτυπωθούν.
Το μόνο που μένει είναι να δημιουργήσουμε μια εκτύπωση που κάνει print με τυχαίο τρόπο κάποιο αντικείμενο της λίστας μας. Τοποθετούμε εντός του print() τη λίστα, και για δείκτη χρησιμοποιούμε τον τυχαίο αριθμό που παράγει η random.randint().
Πρέπει, όμως, να διασφαλίσουμε ότι ο αριθμός που θα παράγει η randint() αντιστοιχεί σε κάποια θέση αντικειμένου της λίστας. Γι' αυτό την καλούμε δίνοντάς της διάστημα από το μηδέν μέχρι έναν αριθμό κάτω από το μήκος της λίστας. Εφόσον η λίστα μας έχει επτά αντικείμενα, ο τυχαίος αριθμός θα είναι από το 0 μέχρι το 6.
Στο επόμενο μάθημα για τον προγραμματισμό Python
Συνεχίζουμε με νέους τύπους δεδομένων, συγγενικούς με τη λίστα, και μαθαίνουμε κάποιες ακόμα χρήσιμες συναρτήσεις, με τις οποίες μπορούμε να τους αξιοποιήσουμε.
Σας άρεσε το σημερινό μάθημα για τον προγραμματισμό Python?
Σιγά-σιγά μαθαίνουμε όλο και μεγαλύτερο μέρος των δομών δεδομένων της Python και μπορούμε να γράφουμε πιο πολύπλοκα προγράμματα. Έχετε δοκιμάσει εσείς να γράψετε κάποιο?
Για οποιαδήποτε πρόταση ή πρόβλημά με τον κώδικά σας, γράψτε μας στα σχόλια.
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.