Στο τέταρτο μάθημα προγραμματισμού με την Python, συνεχίζουμε με ένα από τα πιο βασικά εργαλεία μιας γλώσσας, τις συναρτήσεις. Αν και δεν έχουμε αναφερθεί ως τώρα εκτεταμένα στην έννοια της συνάρτησης, έχουμε έρθει σε επαφή με αρκετές συναρτήσεις όπως οι print(), input(), και len() στα προηγούμενα μαθήματά μας.
Προτάσεις συνεργασίας
Τα νέα άρθρα του PCsteps
Γίνε VIP μέλος στο PCSteps
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.
Τέτοιες συναρτήσεις ανήκουν σε αυτές που μας παρέχονται ενσωματωμένες με την Python. Αυτό σημαίνει ότι κάποιος τις έχει ήδη γράψει για εμάς και μπορούμε κατευθείαν -ή με την εισαγωγή του κατάλληλου module – να τις χρησιμοποιήσουμε.
Τι γίνεται όμως αν χρειαστούμε μια εξειδικευμένη δουλειά, για την οποία δεν υπάρχει συνάρτηση ή αγνοούμε την ύπαρξή της? Στην περίπτωση αυτή, θα πάμε να κατασκευάσουμε μόνοι μας μία συνάρτηση που εξυπηρετεί τις ανάγκες μας.
Για όσους έχασαν το προηγούμενο μάθημα, μπορούν να του ρίξουν μια ματιά πριν το σημερινό.
Κατασκευή συνάρτησης
Ανοίγουμε τον editor μας και γράφουμε τον παρακάτω κώδικα:
Στην πρώτη γραμμή περιέχεται μια δήλωση “def”, η οποία ορίζει μια συνάρτηση που ονομάσαμε “hello()”.
Τα περιεχόμενα του block που ακολουθεί την “def” αποτελούν το σώμα της συνάρτησης. Αυτός ο κώδικας εκτελείται όταν καλούμε την συνάρτηση και όχι όταν ορίζεται η συνάρτηση για πρώτη φορά.
Μετά τη συνάρτηση, στις τελευταίες τρεις γραμμές, έχουμε μερικές κλήσεις της συνάρτησης. Όταν γράφουμε το πρόγραμμά μας, η κλήση μιας συνάρτησης είναι απλά το όνομα της συνάρτησης, ακολουθούμενο από παρενθέσεις, ενδεχομένως και με κάποια ορίσματα εντός των παρενθέσεων.
Μόλις η εκτέλεση του προγράμματος φτάσει σε μία κλήση, θα μεταφερθεί στην πρώτη γραμμή του block κώδικα της συνάρτησης, και θα αρχίσει να τον εκτελεί. Φτάνοντας στο τέλος της συνάρτησης, η εκτέλεση επιστρέφει στη γραμμή που έγινε η κλήση ,και συνεχίζει φυσιολογικά με την επόμενη εντολή του προγράμματος.
Στο παράδειγμά μας, αφού το πρόγραμμα καλεί την hello() δύο φορές, ο κώδικας εντός της συνάρτησης hello() θα εκτελεστεί τρεις φορές. Αν το τρέξουμε, το αποτέλεσμα θα είναι το παρακάτω:
Μία από τις βασικές ανάγκες που εξυπηρετούν οι συναρτήσεις, είναι να ομαδοποιούν κώδικα που εκτελείται πολλές φορές. Αν δεν είχαμε ορίσει την παραπάνω συνάρτηση, θα έπρεπε για το ίδιο αποτέλεσμα να γράψουμε τον ίδιο κώδικα ξανά και ξανά:
Γενικότερα, καλό είναι να αποφεύγεται η επανάληψη κώδικα κατά τη συγγραφή προγραμμάτων. Διαφορετικά, αν θελήσουμε να κάνουμε αλλαγή σε κάποια εντολή (πχ για τη διόρθωση ενός bug), θα πρέπει να θυμόμαστε να την εφαρμόσουμε σε κάθε αντίγραφό της μέσα στο πρόγραμμα.
Δήλωση def με παραμέτρους
Όταν καλούμε μια συνάρτηση όπως η print() και η len(), της περνάμε κάποιες τιμές, τα λεγόμενα ορίσματα, τοποθετώντας τες μέσα στις παρενθέσεις. Ομοίως και η δίκη μας συνάρτηση μπορεί να δέχεται ορίσματα.
Μπορούμε να γράψουμε ένα νέο πρόγραμμα με μια συνάρτηση σαν του προηγούμενου παραδείγματος, με μικρές διαφοροποιήσεις:
Αν τρέξουμε το πρόγραμμα αυτό, θα έχουμε το αποτέλεσμα:
Αυτή τη φορά όταν ορίσαμε τη συνάρτηση hello(), τη συνοδεύσαμε και από μία παράμετρο, τη “name”. Παράμετρος είναι η μεταβλητή στην οποία αποθηκεύεται το όρισμα της συνάρτησης, όταν την καλούμε.
Στην προκειμένη περίπτωση, στην πρώτη κλήση της hello(), το όρισμα που δώσαμε είναι το ‘John'. Όταν η εκτέλεση του προγράμματος φτάσει στη συνάρτηση, η παράμετρός της, δηλαδή η μεταβλητή name, λαμβάνει αυτομάτως την τιμή ‘John'.
Έτσι, οι συγκεκριμένες εκτυπώσεις θα εμφανίσουν όπου name, το ‘John'.
Θα πρέπει να τονιστεί ότι η τιμή του ορίσματος που έλαβε η παράμετρος της συνάρτησης, θα ξεχαστεί όταν η συνάρτηση επιστρέψει.
Για παράδειγμα, αν δοκιμάσουμε να προσθέσουμε ένα “print(name)” μετά την τελευταία κλήση της συνάρτησης, (δηλαδή την “hello(‘Kwstetsos’)” ), το πρόγραμμα θα μας δώσει NameError.
Όπως είπαμε, η μεταβλητή “name” έλαβε προσωρινά μόνο την τιμή του ορίσματος, (δηλαδή το ‘Kwstetsos’) και καταστράφηκε μετά από αυτήν την κλήση της συνάρτησης. Έτσι, στο “print(name)”, το “name” αναφέρεται σε μία μεταβλητή που δεν υπάρχει.
Τιμή επιστροφής – Δήλωση return
Όταν καλούμε τη συνάρτηση len() και της περνάμε ένα όρισμα, όπως το ‘Hello’, η κλήση της συνάρτησης υπολογίζει το αποτέλεσμα στην ακέραια τιμή “5”, δηλαδή στο μήκος του string που δώσαμε.
Γενικότερα, η τιμή που υπολογίζει η κλήση μίας συνάρτησης λέγεται τιμή που επιστρέφει η συνάρτηση (return value).
Μπορούμε να καθορίσουμε την τιμή που επιστρέφει μια συνάρτηση όταν την κατασκευάζουμε με τη δήλωση “def”. Η δήλωση “return” έχει την εξής σύνταξη:
- τη λέξη-κλειδί return
- την τιμή ή έκφραση που επιθυμούμε να επιστρέφει η συνάρτηση. Όταν χρησιμοποιείται μια έκφραση με τη δήλωση “return”, η τιμή επιστροφής θα είναι το αποτέλεσμα στο οποίο εκτιμάται η έκφραση.
Ας πάρουμε για παράδειγμα το ακόλουθο πρόγραμμα, το οποίο ορίζει μια συνάρτηση που επιστρέφει διαφορετικό string, ανάλογα με τον αριθμό που της περνάμε ως όρισμα.
Αρχικά, το πρόγραμμα εισάγει το module “random”.
Στη συνέχεια, ορίζεται η συνάρτηση “get_answer()”. Εφόσον πρόκειται για τη δημιουργία της συνάρτησης (και όχι για κλήση), η εκτέλεση του προγράμματος θα παραλείψει τον κώδικα που περιέχει.
Έπειτα, γίνεται κλήση της συνάρτησης “random.randint()”, με δύο ορίσματα, τα “1” και “7”. Αυτή επιστρέφει έναν τυχαίο ακέραιο μεταξύ 1 και 7 (συμπεριλαμβανομένων και των 1 και 7), και η τιμή αποθηκεύεται στη μεταβλητή “r”.
Η συνάρτηση “get_answer()” καλείται με την “r” ως όρισμα. Η ροή του προγράμματος θα μεταφερθεί στην αρχή της συνάρτησης “get_answer()”, και η τιμή “r” θα αποθηκευτεί στην παράμετρο “answer_number”.
Μετά, ανάλογα με την τιμή της “abswer_number”, η συνάρτηση θα επιστρέψει το αντίστοιχο string, ελέγχοντας με τα if-elif.
Τώρα η ροή θα επανέλθει στο κάτω μέρος του προγράμματος, στη γραμμή όπου έγινε η κλήση της “get_answer()”. Το string που επέστρεψε η συνάρτηση ανατίθεται στην μεταβλητή “luck”, την οποία τελικά περνάμε σε μια κλήση της print().
Σημείωση: Στον προγραμματισμό συχνά προσπαθούμε να γράψουμε μικρότερου μεγέθους κώδικα. Εφόσον, λοιπόν, μπορούμε να περάσουμε τιμές που επιστρέφει μια συνάρτηση ως όρισμα στην κλήση μιας άλλης, θα μπορούσαμε να μειώσουμε τις τρεις γραμμές…
r = random.randint(1, 7) luck = get_answer(r) print(luck)
…σε μία μόνο ισοδύναμη:
print(get_answer(random.randint(1, 7)))
Άλλωστε, οι εκφράσεις αποτελούνται από τιμές και τελεστές. Επομένως, η κλήση μιας συνάρτησης μπορεί να χρησιμοποιηθεί και αυτή σε εκφράσεις, αφού δίνει ως αποτέλεσμα μια τιμή (την return value).
H τιμή None
Στην Python υπάρχει μία τιμή που ονομάζεται “None”, η οποία αντιπροσωπεύει την απουσία τιμής.
Το “None” αποτελεί τη μοναδική τιμή του τύπου δεδομένων NoneType. Σε άλλες γλώσσες προγραμματισμού μπορεί να το συναντήσουμε ως “null”, “nil”, ή “undefined”. Όπως και οι τιμές Boolean “True” και “False”, έτσι και το “None” πρέπει να γράφεται με κεφαλαίο το “N”.
Αυτή η τιμή (που δεν περιέχει καμία τιμή στην ουσία) μπορεί να μας βοηθήσει όταν χρειάζεται να αποθηκεύσουμε κάτι που δεν θα το μπερδέψουμε με αληθινή τιμή σε μια μεταβλητή.
Ένα σημείο στο οποίο χρησιμοποιείται η “None” είναι ως τιμή return σε ένα print(). Η print() εκτυπώνει κείμενο στην οθόνη, αλλά δεν απαιτείται να επιστρέψει κάτι, όπως η len() ή η input().
Εφόσον όμως όλες οι κλήσεις συναρτήσεων πρέπει να εκτιμήσουν μια return value, η print() επιστρέφει τη “None”. Ας το δούμε στην πράξη, γράφοντας το παρακάτω στο κέλυφος:
Αυτό σημαίνει ότι η μεταβλητή έχει λάβει την τιμή “None”. H Python προσθέτει παρασκηνιακά ένα “return None” στο τέλος κάθε δημιουργίας συνάρτησης που δεν έχει δήλωση return. Αυτό θυμίζει τον τρόπο που ένας βρόχος while ή loop τελειώνει ουσιαστικά με ένα continue, ακόμα κι αν δεν έχουμε γράψει εμείς δήλωση continue.
Επίσης, αν χρησιμοποιήσουμε ένα σκέτο return σε μια δήλωση return χωρίς τιμή, η συνάρτηση θα επιστρέψει και πάλι “None”.
Ορίσματα-λέξεις κλειδιά και print()
Τα περισσότερα ορίσματα προσδιορίζονται από τη θέση τους στην κλήση της συνάρτησης. Για παράδειγμα, το random.randint(1, 7) είναι διαφορετικό από το random.randint(7, 1).
Η κλήση της συνάρτησης random.randint(1, 7) θα επιστρέψει έναν τυχαίο ακέραιο μεταξύ 1 και 7, καθώς το πρώτο όρισμα είναι το χαμηλό άκρο του διαστήματος και το δεύτερο το υψηλό. Αντίθετα, το random.randint(7, 1) θα προκαλέσει error.
Όταν, ωστόσο, χρησιμοποιούμε ορίσματα-λέξεις κλειδιά, αυτά προσδιορίζονται από τη λέξη-κλειδί που βρίσκεται μπροστά τους στην κλήση της συνάρτησης. Τα ορίσματα αυτά συχνά χρησιμοποιούνται ως προαιρετικές παράμετροι.
Για παράδειγμα, η συνάρτηση print() έχει τις προαιρετικές παραμέτρους “end” και “sep” για να καθορίσει τι θα εκτυπωθεί στο τέλος των ορισμάτων (για το “end”), και ανάμεσα στα ορίσματα (για το “sep”).
Αν τρέξουμε το παρακάτω πρόγραμμα:
…θα πάρουμε την εκτύπωση:
Όπως διαπιστώνουμε, τα δύο string εμφανίζονται σε ξεχωριστές γραμμές, καθώς η συνάρτηση print() αυτομάτως προσθέτει στο τέλος του string που της περάσαμε έναν χαρακτήρα newline. Πρόκειται για έναν χαρακτήρα που μόλις διαβαστεί από την Python, η εκτύπωση προχωράει στην επόμενη γραμμή.
Παρ’ όλα αυτά, μπορούμε να προσθέσουμε το όρισμα-λέξη κλειδί “end” για να αλλάξουμε το newline στον χαρακτήρα της αρεσκείας μας. Θέτοντάς το στο κενό string (”), η print() αντί να γράψει τον χαρακτήρα newline στην οθόνη (δηλαδή δεν θα αλλάξει γραμμή), θα γράψει το κενό string (δηλαδή τίποτα).
Το αποτέλεσμα τυπώνεται λοιπόν σε μία γραμμή, κάτι που θα μας φανεί χρήσιμο αν θέλουμε διαδοχικές εκτυπώσεις να είναι ενιαίες χωρίς να αλλάζουν γραμμή.
Με παρόμοιο τρόπο, έχουμε δει ότι όταν περνάμε πάνω από ένα string στην print(), η συνάρτηση εισάγει και ένα κενό ανάμεσά τους. Γράφοντας για παράδειγμα στον IDLE το παρακάτω, θα έχουμε την εκτύπωση:
Αντικαθιστούμε τώρα το προεπιλεγμένο κενό, που είναι και αυτό ένα string, προσθέτοντας ένα όρισμα “sep”:
Σε επόμενο μάθημα θα δούμε ότι μπορούμε να προσθέσουμε ορίσματα-λέξεις κλειδιά και στις συναρτήσεις που κατασκευάζουμε μόνοι μας. Θα πρέπει πρώτα όμως να μάθουμε κάποιους ακόμα τύπους δεδομένων, τη λίστα (list) και το λεξικό (dictionary).
Παράδειγμα προγράμματος
Μπορούμε πλέον να εξετάσουμε ένα πιο συνδυαστικό πρόγραμμα, που χρησιμοποιεί διάφορες βασικές έννοιες που έχουμε μάθει ως τώρα. Πρόκειται για συχνό παράδειγμα προγράμματος σε εισαγωγικά μαθήματα προγραμματισμού: το παιχνίδι “Guess the number”.
Σε αυτό, ο ένας παίκτης (ο υπολογιστής στο πρόγραμμά μας) σκέφτεται και καταγράφει έναν αριθμό, ενώ ο άλλος προσπαθεί να τον βρει, ρωτώντας τον διάφορους αριθμούς.
Το πρόγραμμα απαντάει σε κάθε πρόταση του παίκτη αν ο αριθμός είναι μεγαλύτερος, μικρότερος, ή ο ίδιος με τον δικό του. Σκοπός του παίκτη που μαντεύει είναι να βρει τον ζητούμενο αριθμό, πριν τελειώσουν οι διαθέσιμες “μαντεψιές” του προγράμματος.
Γράφουμε λοιπόν τον κώδικά μας:
#"Guess the number" game import random secret_number = random.randint(1, 100) print('Τhinking of a number between 1 and 100...') for guesses_taken in range(1, 7): #Ask the player to guess 6 times. print('Please make a guess.') guess = int(input()) if guess < secret_number: print('Your guess is too low.') elif guess > secret_number: print('Your guess is too high.') else: break # This is the correct guess if guess == secret_number: print('Good job. You guessed my number in ' + str(guesses_taken) + ' guesses') else: print('Nice try but the number I was thinking of was ' + str(secret_number))
Τρέχοντας το πρόγραμμα στον IDLE, αν βρούμε τον αριθμό, το αποτέλεσμα θα μοιάζει με κάτι τέτοιο:
Αν πάλι αποτύχουμε:
Ας αναλύσουμε λοιπόν τον κώδικά μας γραμμή – γραμμή, ξεκινώντας από την αρχή του.
#"Guess the number" game import random secret_number = random.randint(1, 100)
Στην πρώτη γραμμή, έχουμε ένα τυπικό σχόλιο που εξηγεί τι κάνει το συγκεκριμένο πρόγραμμα.
Αμέσως μετά εισάγουμε το module “random”, ώστε να μπορέσουμε να χρησιμοποιήσουμε τη συνάρτηση random.randint() ως γεννήτρια του μυστικού αριθμού. Η τιμή που επιστρέφει η συνάρτηση, ανατίθεται στην μεταβλητή secret_number.
print('I am thinking of a number between 1 and 100.') for guesses_taken in range(1, 7): #Ask the player to guess 6 times. print('Please make a guess.') guess = int(input())
Το πρόγραμμα ενημερώνει τον παίκτη ότι έχει σκεφτεί έναν αριθμό. Ο κώδικας που επιτρέπει στον παίκτη να μαντέψει έναν αριθμό και ελέγχει τον αριθμό αυτόν, βρίσκεται εντός ενός βρόχου for.
Ο βρόχος θα επαναληφθεί το μέγιστο έξι φορές, επομένως έξι θα είναι και οι διαθέσιμες προσπάθειες του παίκτη.
Εφόσον, όπως ήδη γνωρίζουμε, η input() επιστρέφει string, περνάμε κατευθείαν την εισαγόμενη τιμή σε μία int(), για να τη μετατρέψουμε σε ακέραιο και την αποθηκεύουμε στη μεταβλητή “guess” .
if guess < secret_number: print('Your guess is too low.') elif guess > secret_number: print('Your guess is too high.')
Με το if αυτό, ελέγχουμε αν η υπόθεση του παίκτη είναι μικρότερη ή μεγαλύτερη από τον μυστικό αριθμό, εκτυπώνοντας στην εκάστοτε περίπτωση το ανάλογο μήνυμα. Αν όμως δεν ισχύει τίποτα από τα δύο, δεν μένει παρά ο παίκτης να έχει βρει τον σωστό αριθμό.
else: break # This is the correct guess
Έτσι, αξιοποιούμε ένα break προκειμένου να σταματήσουμε τον βρόχο, αφού δεν απαιτείται πια άλλη επανάληψη για να κάνει νέα εικασία ο παίκτης.
if guess == secret_number: print('Good job. You guessed my number in ' + str(guesses_taken) + ' guesses') else: print('Nice try but the number I was thinking of was ' + str(secret_number))
Τέλος, βγαίνοντας από τον βρόχο ενημερώνουμε με τη χρήση μιας δήλωσης if…else, αν ο παίκτης εντόπισε τον αριθμό.
Μιας και εμπεριέχεται και στις δύο εκτυπώσεις μεταβλητή ακέραιου αριθμού, την οποία πρέπει να συνενώσουμε με τα υπόλοιπα string, τις περνάμε πρώτα από την str().
Στο επόμενο μάθημα για τον προγραμματισμό Python
Γνωρίζοντας πλέον τις συναρτήσεις, είναι ευκαιρία να ασχοληθούμε με την έννοια της εμβέλειας των παραμέτρων και των μεταβλητών. Θα δούμε επίσης πώς μπορούμε – σε κάποιο βαθμό, να κάνουμε διαχείριση των σφαλμάτων του προγράμματός μας.
Σας άρεσε το σημερινό μάθημα για τον προγραμματισμό Python?
Κατασκευάζοντας τις δικές μας συναρτήσεις, είμαστε σε θέση να δημιουργήσουμε αρκετά πιο σύνθετα προγράμματα, με πιο ενδιαφέρουσες λειτουργίες. Γράψτε μας αν φτιάχνετε δικά σας προγράμματα ή αν συναντήσετε κάποιο πρόβλημα.
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.