Warning: Since the translation of this document, many things have changed in Python, making it outdated. Use at your own risk. :-)

Άμεση Python

(Instant Python)

Αυτή είναι μια μινιμαλιστική εισαγωγή στη γλώσσα προγραμματισμού Python. Για να μάθετε περισσότερα, ρίξτε μια ματιά στην τεκμηρίωση στην ιστοσελίδα της Python, http://www.python.org/· ειδικά στο tutorial. Αν αναρωτιέστε γιατί θα πρέπει να ενδιαφερθείτε, ελέγξτε τη σελίδα σύγκρισης όπου η Python συγκρίνεται με άλλες γλώσσες.

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

Τα Βασικά

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

x, y, z = 1, 2, 3

first, second = second, first

a = b = 123

Τα blocks υποδεικνύονται μέσω της εσοχής του κείμενου (indentation) και μόνο. (Όχι BEGIN/END ή αγκύλες.) Μερικές συνηθισμένες δομές ελέγχου είναι:

if x < 5 or (x > 10 and x < 20):
    print "Η τιμή είναι εντάξει."

if x < 5 or 10 < x < 20:
    print "Η τιμή είναι εντάξει."

for i in [1, 2, 3, 4, 5]:
    print "Αυτός είναι ο αριθμός επανάληψης", i

x = 10
while x >= 0:
    print "ο x εξακολουθεί να είναι μη-αρνητικός."
    x = x - 1

Τα πρώτα δύο παραδείγματα είναι ισοδύναμα.

Η μεταβλητή–δείκτης που δίνεται στο βρόχο for εκτελεί επαναλήψεις μέσα στα στοιχεία μιας λίστας (που γράφεται όπως στο παράδειγμα). Για να φτιάξετε ένα “συνηθισμένο” βρόχο for (δηλαδή ένα βρόχο μέτρησης), χρησιμοποιείστε την ενσωματωμένη (built–in) συνάρτηση range().

# εκτύπωσε τις τιμές από το 0 μέχρι και το 99
for value in range(100):
    print value

(Η γραμμή που ξεκινάει με # είναι ένα σχόλιο και αγνοείται από το διερμηνέα.)

Ωραία· τώρα γνωρίζετε αρκετά για να υλοποιήσετε (θεωρητικά) οποιονδήποτε αλγόριθμο στην Python. Ας προσθέσουμε μερική βασική αλληλεπίδραση με τον χρήστη. Για να πάρετε είσοδο από τον χρήστη (από ένα νεύμα/prompt κειμένου), χρησιμοποιήστε την ενσωματωμένη συνάρτηση input.

x = input("Παρακαλώ δώστε έναν αριθμό: ")
print "Το τετράγωνο αυτού του αριθμού είναι", x * x

Η συνάρτηση input εμφανίζει το νεύμα που δώθηκε (το οποίο μπορεί να είναι άδειο) και αφήνει το χρήστη να εισάγει οποιαδήποτε έγκυρη τιμή Python. Σε αυτή την περίπτωση αναμένουμε έναν αριθμό — αν εισαχθεί κάτι άλλο (όπως ένα αλφαριθμητικό), το πρόγραμμα θα “χτυπήσει” (crash). Για να αποφθεχθεί αυτό θα χρειαστούμε μερικό έλεγχο σφαλμάτων. Δε θα αναφερθώ σε αυτό εδώ· θα αρκεστώ στο να πω ότι αν θέλετε την είσοδο του χρήστη αποθηκευμένη αυτολεξεί ως ένα αλφαριθμητικό (έτσι ώστε να μπορεί να εισαχθεί οτιδήποτε), χρησιμοποιήστε τη συνάρτηση raw_input. Αν θέλετε να μετατρέψετε ένα αλφαριθμητικό εισόδου s σε ακέραιο, μπορείτε να χρησιμοποιήσετε int(s).

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

Λοιπόν, έχουμε καλύψει τις δομές ελέγχου, την είσοδο και την έξοδο — τώρα χρειαζόμαστε μερικές κομψές δομές δεδομένων. Οι πιο σημαντικές είναι οι λίστες και τα λεξικά. Οι λίστες γράφονται με αγκύλες, και μπορούν (φυσικά) να είναι εμφωλευμένες:

name = ["Παπαδόπουλος", "Γιάννης"]

x = [[1, 2, 3], [y, z], [[[]]]]

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

print name[1], name[0] # εκτυπώνει "Γιάννης Παπαδόπουλος"

name[0] = "Παπανικολάου"

Η διαμέριση είναι σχεδόν όπως η δεικτοδότηση, εκτός από το ότι υποδεικνύετε τόσο τον αρχικό όσο καί τον τελικό δείκτη του αποτελέσματος, με μια άνω–κάτω τελεία (:) να τους χωρίζει:

x = ["spam", "spam", "spam", "spam", "spam", "αυγά", "και", "spam"]

print x[5:7] # εκτυπώνει τη λίστα ["αυγά", "και"]

Προσέξτε ότι το τέλος δεν περιλαμβάνεται. Αν κάποιος από τους δείκτες παραλειφθεί, γίνεται η υπόθεση ότι θέλετε τα πάντα προς αυτή την κατεύθυνση. Π.χ. list[:3] σημαίνει “κάθε στοιχείο από την αρχή της list μέχρι το στοιχείο 3, χωρίς αυτό.” (Θα μπορούσε να υποστηριχθεί ότι στην πραγματικότητα θα σήμαινε 4, μιας και το μέτρημα αρχίζει από το 0.) list[3:], από την άλλη μεριά, θα σήμαινε “κάθε στοιχείο από την list, αρχίζοντας από το στοιχείο 3 (περιλαμβάνοντάς το) έως και το τελευταίο”. Για πραγματικά ενδιαφέροντα αποτελέσματα, μπορείτε να χρησιμοποιήσετε αρνητικούς αριθμούς επίσης: list[-3] είναι το τρίτο στοιχείο από το τέλος της list

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

Τώρα, λοιπόν — τι γίνεται με τα λεξικά; Για να το θέσω απλά, είναι σαν λίστες, μόνο που τα περιεχόμενά τους δεν είναι ταξινομημένα. Πώς τα δεικτοδοτείς λοιπόν; Κάθε στοιχείο έχει ένα κλειδί, ή ένα “όνομα” το οποίο χρησιμοποιείται για να βρεθεί το στοιχείο όπως ακριβώς σε ένα πραγματικό λεξικό. Μερικά παραδείγματα λεξικών:

{"Αγγελική": 23452532, "Βαγγέλης": 252336, "Γεωργία": 2352525, "Δώρα": 23624643}

person = {'όνομα': "Ρομπέν", 'επίθετο': "των Δασών", 'απασχόληση': "άνεργος"}

Τώρα, για να πάρουμε το επάγγελμα του person, χρησιμοποιούμε την έκφραση person["απασχόληση"]. Αν θέλαμε να αλλάξουμε το επίθετό του, θα μπορούσαμε να γράψουμε:

person['επίθετο'] = "του Locksley"

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

Συναρτήσεις

Επόμενο βήμα: Αφαίρεση. Θέλουμε να δώσουμε ένα όνομα σε ένα κομμάτι κώδικα, και να το καλέσουμε με μερικές παραμέτρους. Με άλλα λόγια — θέλουμε να ορίσουμε μια συνάρτηση (ή “διαδικασία”). Αυτό είναι εύκολο. Χρησιμοποιήστε τη λέξη–κλειδί def ως εξής:

def square(x):
    return x * x

print square(2) # εκτυπώνει 4

Για εκείνους από σας που το καταλαβαίνετε: Όταν περνάτε μια παράμετρο σε μια συνάρτηση, “δένετε” (bind) την παράμετρο στην τιμή, δημιουργώντας έτσι μια νέα αναφορά (reference). Αν αλλάξετε τα “περιεχόμενα” αυτού του ονόματος παραμέτρου (δηλαδή το “επαναδέσετε”) αυτό δε θα επηρεάσει την αρχική. Αυτό δουλεύει ακριβώς όπως στη Java, για παράδειγμα. Ας ρίξουμε μια ματιά σε ένα παράδειγμα:

def change(some_list):
    some_list[1] = 4

x = [1, 2, 3]
change(x)
print x # εκτυπώνει [1, 4, 3]

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

def nochange(x):
    x = 0

y = 1
nochange(y)
print y # εκτυπώνει 1

Γιατί δεν υπάρχει αλλαγή τώρα; Διότι δεν αλλάζουμε την τιμή! Η τιμή που περνιέται είναι ο αριθμός 1 — δε μπορούμε να αλλάξουμε έναν αριθμό με τον ίδιο τρόπο που αλλάζουμε μια λίστα. Ο αριθμός 1 είναι (και θα είναι πάντα) ο αριθμός 1. Αυτό που όντως κάναμε είναι η αλλαγή των περιεχομένων της τοπικής μεταβλητής (παράμετρου) x, και αυτό δεν “κουβαλιέται” στο περιβάλλον.

Για εκείνους από σας που δεν το κατάλαβαν: Μην ανησυχείτε — είναι αρκετά εύκολο αν δεν το σκέφτεστε πολύ. :)

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

Αν γνωρίζετε πώς να χρησιμοποιείτε συναρτήσεις γενικά, αυτό είναι βασικά αυτό που χρειάζεται να ξέρετε για αυτές στην Python. (Ω, ναι… Η λέξη–κλειδί return σταματάει την εκτέλεση της συνάρτησης και επιστρέφει τη δοσμένη τιμή.)

Ένα πράγμα που θα ήταν χρήσιμο να γνωρίζετε, παρόλα αυτά, είναι ότι οι συναρτήσεις είναι τιμές στην Python. Έτσι, αν έχετε μια συνάρτηση όπως η square, μπορείτε να κάνετε κάτι σαν:

queeble = square
print queeble(2) # εκτυπώνει 4

Για να καλέσετε μια συνάρτηση χωρίς ορίσματα πρέπει να θυμάστε να γράφετε doit() και όχι doit. Το τελευταίο, όπως δείχθηκε, επιστρέφει μόνο την ίδια τη συνάρτηση, ως τιμή. (Αυτό ισχύει και για τις μεθόδους σε αντικείμενα επίσης… Δείτε παρακάτω.)

Αντικείμενα

Υποθέτω ότι γνωρίζετε πώς δουλεύει ο αντικειμενοστρεφής προγραμματισμός. (Διαφορετικά, αυτός ο τομέας ενδέχεται να μην έχει και πολύ νόημα για σας. Κανένα πρόβλημα… Αρχίστε να παίζετε χωρίς τα αντικείμενα. :)) Στην Python καθορίζετε κλάσεις με τη λέξη–κλειδί (έκπληξη!) class, έτσι:

class Basket:

    # πάντα να θυμάστε το όρισμα self
    def __init__(self, contents=None):
        self.contents = contents or []

    def add(self, element):
        self.contents.append(element)

    def print_me(self):
        result = ""
        for element in self.contents:
            result = result + " " + `element`
        print "Περιέχει: " + result

Νέα πράγματα εδώ:

  1. Όλες οι μέθοδοι (συναρτήσεις σε ένα αντικείμενο) δέχονται ένα επιπλέον όρισμα στην αρχή της λίστας ορισμάτων, το οποίο περιέχει το ίδιο το αντικείμενο. (Λέγεται self σε αυτό το παράδειγμα και είναι θέμα επιλογής.)
  2. Οι μέθοδοι καλούνται ως εξής: object.method(arg1, arg2).
  3. Μερικά ονόματα μεθόδων, όπως __init__ (με δύο underscore πριν και μετά), είναι προκαθορισμένες, και σημαίνουν ξεχωριστά πράγματα. __init__ είναι το όνομα του constructor της κλάσης, δηλαδή είναι η συνάρτηση η οποία καλείται όταν δημιουργείτε ένα στιγμιότυπο.
  4. Μερικά ορίσματα μπορεί να είναι προαιρετικά και να τους δωθεί προκαθορισμένη τιμή (όπως αναφέρθηκε πριν, στον τομέα των συναρτήσεων). Αυτό γίνεται γράφοντας τον ορισμό σαν:
    def spam(age=32): ...
    Εδώ, η spam μπορεί να κληθεί με μία ή μηδέν παραμέτρους. Αν καμία δε χρησιμοποιηθεί, τότε η παράμετρος age θα έχει την τιμή 32.
  5. “Λογική βραχυκυκλώματος.” Αυτό είναι έξυπνο… Δείτε παρακάτω.
  6. Τα ανάποδα μονά εισαγωγικά μετατρέπουν ένα αντικείμενο στην αλφαριθμητική του αναπαράσταση. (Έτσι αν το element περιέχει τον αριθμό 1, τότε το `element` είναι το ίδιο με "1" ενώ το 'element' είναι ένα ακριβές/literal αλφαριθμητικό.)
  7. Το σύμβολο της πρόσθεσης + χρησιμοποιείται επίσης για την ένωση λιστών, και τα αλφαριθμητικά στην πραγματικότητα είναι απλά λίστες χαρακτήρων (που σημαίνει ότι μπορείτε να χρησιμοποιήσετε δεικτοδότηση και διαμέριση και τη συνάρτηση len σε αυτά. Καλό, ε;)

Καμία μέθοδος ή μεταβλητή μεθόδου δεν είναι προστατευμένη (ή ιδιωτική ή κάτι παρόμοιο) στην Python. Η ενθυλάκωση (encapsulation) είναι αρκετά θέμα προγραμματιστικού στυλ. (Αν το χρειάζεστε πραγματικά, υπάρχουν ονομαστικές συμβάσεις που θα επιτρέψουν λίγη “απομόνωση”.)

Τώρα, σχετικά με αυτή τη λογική βραχυκυκλώματος…

Όλες οι τιμές στην Python μπορούν να χρησιμοποιηθούν ως λογικές τιμές. Μερικές από τις πιο “άδειες”, όπως [], 0, "" και None αναπαριστούν λογική αναλήθεια (ψεύδος), ενώ οι περισσότερες άλλες τιμές (όπως [0], 1 ή "Γεια σου, κόσμε!") αναπαριστούν λογική αλήθεια.

Τώρα, λογικές εκφράσεις όπως a and b εκτιμώνται ως εξής: Πρώτα, έλεγξε αν η a είναι αληθής. Αν δεν είναι, τότε απλά επέστρεψέ τη. Αν είναι, τότε απλά επέστρεψε τη b (η οποία θα αναπαριστά την τιμή αληθείας της έκφρασης). Η αντίστοιχη λογική για a or b είναι: Αν η a είναι αληθής, τότε επέστρεψέ τη. Αν δεν είναι, τότε επέστρεψε τη b.

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

if a:
    print a
else:
    print b

Θα μπορούσε να γραφεί διαφορετικά:

print a or b

Στην πραγματικότητα, αυτό είναι κάτι σαν ιδίωμα της Python, έτσι ενδέχεται κάλλιστα να το συνηθίσετε. Αυτό είναι εκείνο που κάνουμε στη μέθοδο Basket.__init__. Το όρισμα contents έχει μια προκαθορισμένη τιμή None (που είναι, πέρα από άλλα, ψευδής). Έτσι, για να ελέγχαμε αν είχε μια τιμή, θα μπορούσαμε να γράψουμε:

if contents:
    self.contents = contents
else:
    self.contents = []

Φυσικά, τώρα γνωρίζετε ότι υπάρχει ένας καλύτερος τρόπος. Και γιατί δεν του δίνουμε την προκαθορισμένη τιμή [] εξ αρχής; Λόγω του τρόπου με τον οποίο δουλεύει η Python, αυτό θα έδινε σε όλα τα Basket την ίδια άδεια λίστα ως προκαθορισμένο περιεχόμενο. Μόλις κάποιο από αυτά άρχιζε να γεμίζει, όλα θα περιείχαν τα ίδια στοιχεία, και το προκαθορισμένο δε θα ήταν πλέον άδειο… Για να μάθετε περισσότερα σχετικά με αυτό, πρέπει να διαβάσετε την τεκμηρίωση και να ψάξετε για τη διαφορά μεταξύ ταυτότητας και ισότητας.

Ένας άλλος τρόπος για να γίνουν τα παραπάνω είναι:

def __init__(self, contents=[]):
    self.contents = contents[:]

Μπορείτε να μαντέψετε πώς δουλεύει αυτό; Αντί να χρησιμοποιούμε την ίδια άδεια λίστα παντού, χρησιμοποιούμε την έκφραση contents[:] για να φτιάξουμε ένα αντίγραφο. (Απλά διαμερίζουμε το όλο πράγμα.)

Έτσι, για να φτιάχναμε πραγματικά ένα Basket και να το χρησιμοποιούσαμε (δηλαδή να καλούσαμε μερικές μεθόδους του) θα κάναμε κάτι σαν αυτό:

b = Basket(['μήλο', 'πορτοκάλι'])
b.add("λεμόνι")
b.print_me()

Υπάρχουν και άλλες μαγικές μέθοδοι εκτός από την __init__. Μία τέτοια μέθοδος είναι η __str__ η οποία καθορίζει πώς το αντικείμενο θέλει να φαίνεται αν αντιμετωπιστεί σαν ένα αλφαριθμητικό. Θα μπορούσαμε να χρησιμοποιήσουμε αυτό στο καλάθι μας αντί για την print_me:

def __str__(self):
    result = ""
    for element in self.contents:
        result = result + " " + `element`
    return "Contains:" + result

Τώρα, αν θέλαμε να εκτυπώσουμε το καλάθι b, θα μπορούσαμε απλά να χρησιμοποιήσουμε:

print b

Καλό, ε;

Το subclassing γίνεται ως εξής:

class SpamBasket(Basket):
    # ... 

Η Python επιτρέπει πολλαπλή κληρονομικότητα, έτσι μπορείτε να έχετε αρκετές υπερκλάσεις στις παρανθέσεις, χωρισμένες με κόμματα. Οι κλάσεις αρχικοποιούνται ως εξής: x = Basket(). Οι constructors, όπως είπα, φτιάχνονται ορίζοντας την ειδική συνάρτηση μέλους __init__. Ας πούμε ότι το SpamBasket είχε έναν __init__(self, type). Τότε θα μπορούσατε να φτιάξετε ένα καλάθι spam ως εξής: y = SpamBasket("μήλα").

Αν, στον constructor της SpamBasket, χρειαζόσασταν να καλέσετε τον constructor μιας ή περισσότερων υπερκλάσεων, θα μπορούσατε να τον καλέσετε ως εξής: Basket.__init__(self). Σημειώστε ότι, παράλληλα με την παροχή των συνηθισμένων παραμέτρων, πρέπει να παρέχετε ρητά τη self, μιας και η υπερκλάση __init__ δε γνωρίζει με ποιό στιγμιότυπο έχει να κάνει.

Για περισσότερα σχετικά με τα θαύματα του αντικειμενοστρεφή προγραμματισμού στην Python, δείτε τον τομέα 9 του tutorial.

Ένα “Jedi Mind” Κόλπο

(Αυτός ο τομέας είναι εδώ μόνο επειδή νομίζω ότι είναι ωραίος. Δεν είναι απαραίτητο να τον διαβάσετε για να αρχίσετε να μαθαίνετε την Python. Δείτε το τέλος του τομέα για μια σημείωση σχετικά με αλλαγές για την Python 2.1.)

Σας αρέσουν οι έννοιες που κάνουν το μυαλό να “σαστίζει”; Τότε, αν είστε πραγματικά τολμηροί, ίσως να θελήσετε να ελέγξετε την έκθεση του Guido van Rossum στις μετακλάσεις. Αν, όμως, προτιμάτε να μην εκραγεί το μυαλό σας, ίσως να ικανοποιηθείτε με αυτό το μικρό κόλπο.

Η Python χρησιμοποιεί δυναμικούς αντί για λεκτικούς χώρους ονομάτων (namespaces). Αυτό σημαίνει ότι αν έχετε μια συνάρτηση σαν αυτή:

def orange_juice():
    return x * 2

… όπου μια μεταβλητή (σε αυτή την περίπτωση η x) δεν είναι “δεμένη” σε ένα όρισμα και δεν της δίνεται μια τιμή μέσα στη συνάρτηση, η Python θα χρησιμοποιήσει την τιμή που έχει όπου και όταν η συνάρτηση κληθεί. Σε αυτή την περίπτωση:

x = 3
y = orange_juice()
# η y είναι τώρα 6
x = 1
y = orange_juice()
# η y είναι τώρα 2

Συνήθως, αυτό είναι το είδος της συμπεριφοράς που θέλετε (αν και το πάνω παράδειγμα είναι λίγο contrived — σπάνια η πρόσβασή σας σε μεταβλητές είναι έτσι.) Όμως, μερικές φορές μπορεί να είναι ωραίο να έχετε κάτι σαν ένα στατικό χώρο ονομάτων, δηλαδή, την αποθήκευση μερικών τιμών από το περιβάλλον στη συνάρτηση όταν αυτή δημιουργείται. Ο τρόπος για να το κάνετε αυτό στην Python είναι μέσω των προκαθορισμένων ορισμάτων.

x = 4
def apple_juice(x=x):
    return x * 2

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

x = 3
y = apple_juice()
# η y είναι τώρα 8
x = 1
y = apple_juice()
# η y είναι τώρα 8

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

def tomato_juice():
    x = 4
    return x * 2

ή ακόμα και

def carrot_juice():
    return 8

Όμως, το νόημα είναι ότι η τιμή της x λαμβάνεται από το περιβάλλον τη στιγμή που η συνάρτηση ορίζεται. Πώς είναι αυτό χρήσιμο; Ας πάρουμε ένα παράδειγμα — μια συνάρτηση η οποία συνθέτει δύο άλλες συναρτήσεις.

Θέλουμε μια συνάρτηση που να δουλεύει ως εξής:

from math import sin, cos

sincos = compose(sin, cos)

x = sincos(3)

Όπου compose είναι η συνάρτηση που θέλουμε να φτιάξουμε, και η x έχει την τιμή -0.836021861538, που είναι το ίδιο με sin(cos(3)). Τώρα, πώς το κάνουμε αυτό;

(Σημειώστε ότι χρησιμοποιούμε συναρτήσεις ως ορίσματα εδώ… Αυτό είναι ένα αρκετά καλό κόλπο από μόνο του.)

Φανερά, η compose παίρνει δύο συναρτήσεις ως παράμετρους, και επιστρέφει μια συνάρτηση η οποία ξαναπαίρνει μια παράμετρο. Έτσι, μια λύση–σκελετός θα μπορούσε να είναι:

def compose(fun1, fun2):
    def inner(x):
        pass # ...
    return inner

Θα μπορούσαμε να μπούμε στον πειρασμό να βάλουμε return fun1(fun2(x)) μέσα στη συνάρτηση inner και να το αφήσουμε έτσι. Όχι, όχι, όχι. Αυτό θα συμπεριφερόταν πολύ παράξενα. Φανταστείτε το ακόλουθο σενάριο:

from math import sin, cos

# λανθασμένη εκδοχή
def compose(fun1, fun2):
    def inner(x):
        return fun1(fun2(x))
    return inner

def fun1(x):
    return x + " κόσμε!"

def fun2(x):
    return "Γεια σου,"

sincos = compose(sin, cos) # χρησιμοποιώντας τη λανθασμένη εκδοχή

x = sincos(3)

Τώρα, τι τιμή θα είχε η x; Σωστά: "Γεια σου, κόσμε!". Γιατί συμβαίνει αυτό; Διότι όταν καλείται, λαμβάνει τις τιμές της fun1 και fun2 από το περιβάλλον, όχι αυτές που βρίσκονταν τριγύρω όταν δημιουργήθηκε. Για να πάρουμε μια λύση που να δουλεύει, το μόνο που έχουμε να κάνουμε είναι να χρησιμοποιήσουμε την τεχνική που περιέγραψα νωρίτερα:

def compose(fun1, fun2):
    def inner(x, fun1=fun1, fun2=fun2):
        return fun1(fun2(x))
    return inner

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

def compose(f1, f2):
    return lambda x, f1=f1, f2=f2: f1(f2(x))

Λακωνικό, αλλά καθαρό. Θα το λατρέψετε. :)

(Και αν δεν καταλάβατε τίποτα από αυτό, μην ανησυχείτε. Τουλάχιστον ελπίζω ότι σας έπεισε ότι η Python είναι περισσότερο από "απλά μια γλώσσα scripting"… :))

Μια σημείωση για την Python 2.1 και τα εμφωλευμένα πεδία

Με τον ερχομό της Python 2.1, η γλώσσα τώρα έχει (προαιρετικά) στατικά εμφωλευμένα πεδία ή χώρους ονομάτων. Αυτό σημαίνει ότι μπορείτε να κάνετε τα πράγματα που περιγράφονται στον τομέα αυτό χωρίς μερικά από τα κόλπα. Πλέον μπορείτε απλά να γράφετε:

# αυτό το "μαγικό" δε θα είναι απαραίτητο από την Python 2.2
from __future__ import nested_scopes

def compose(fun1, fun2):
    def inner(x):
        return fun1(fun2(x))
    return inner

… και θα δουλέψει όπως πρέπει.

Και τώρα…

Λίγα πράγματα κοντά στο τέλος. Οι πιο χρήσιμες συναρτήσεις και κλάσεις τοποθετούνται σε μονάδες (modules), που στην πραγματικότητα είναι αρχεία κειμένου που περιέχουν κώδικα. Μπορείτε να τις εισάγετε και να τις χρησιμοποιήσετε στα ίδια σας τα προγράμματα. Για παράδειγμα, για να χρησιμοποιήσετε τη μέθοδο split από την πρότυπη (standard) μονάδα string, μπορείτε να κάνετε:

import string

x = string.split(y)

Ή…

from string import split

x = split(y)

Σημείωση: Η μονάδα string δεν χρησιμοποιείται και πολύ πλέον· αντί για τον κώδικα παραπάνω, πρέπει να χρησιμοποιείτε x = y.split().

Για περισσότερες πληροφορίες σχετικά με τις πρότυπες μονάδες βιβλιοθήκης, ρίξτε μια ματιά στο http://www.python.org/doc/lib/. Περιέχουν πολλά χρήσιμα πράγματα.

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

if __name__ == "__main__": go()

Αυτός είναι ένας μαγικός τρόπος για να λέτε ότι αν η μονάδα αυτή εκτελεστεί ως script (δηλαδή, δεν εισάγεται μέσα σε κάποιο άλλο script), τότε η συνάρτηση go πρέπει να κληθεί. Φυσικά, Θα μπορούσατε να κάνετε οτιδήποτε μετά την άνω–κάτω τελεία εκεί… :)

Και για όσους από σας που θέλετε να κάνετε ένα εκτελέσιμο script σε UN*X, χρησιμοποιήστε την ακόλουθη πρώτη γραμμή για να το κάνετε να τρέχει από μόνο του:

#!/usr/bin/env python

Τέλος, μια σύντομη αναφορά σε μια σημαντική έννοια: Εξαιρέσεις. Μερικές λειτουργίες (όπως η διαίρεση με το μηδέν ή η ανάγνωση από ένα αρχείο που δεν υπάρχει) παράγουν μια συνθήκη σφάλματος ή εξαίρεση. Μπορείτε ακόμα και να φτιάξετε δικές σας και να τις ενεργοποιήσετε (raise) στις σωστές χρονικές στιγμές.

Αν δε γίνει τίποτα για την εξαίρεση, το πρόγραμμά σας τελειώνει και εκτυπώνει ένα μήνυμα σφάλματος. Μπορείτε να το αποφύγετε αυτό με μια try/except–πρόταση. Για παράδειγμα:

def safe_division(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None

Η ZeroDivisionError είναι μια πρότυπη εξαίρεση. Στην περίπτωση αυτή, θα μπορούσατε να είχατε ελέγξει αν η b ήταν μηδέν, αλλά σε πολλές περιπτώσεις, αυτή η στρατηγική δεν είναι εφικτή. Και εξάλλου, αν δεν είχαμε την ενότητα (clause) try στη safe_division, κάνοντάς την έτσι μια ριψοκίνδυνη συνάρτηση προς κλήση, θα μπορούσαμε ωστόσο να κάνουμε κάτι σαν:

try:
    unsafe_division(a, b)
except ZeroDivisionError:
    print "Κάτι διαιρέθηκε με μηδέν στην unsafe_division"

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

Λοιπόν — αυτό είναι. Ελπίζω ότι μάθατε κάτι. Τώρα πηγαίντε και παίξτε. Και θυμηθείτε το μότο εκμάθησης της Python: "Use the source, Luke." (Μετάφραση: Διαβάστε ό,τι κώδικα πέσει στα χέρια σας :)). Για αρχή, ορίστε ένα παράδειγμα. Είναι ο γνωστός αλγόριθμος QuickSort του Hoare. Μια έκδοση με χρώματα στη σύνταξη βρίσκεται εδώ.

Ένα πράγμα που ίσως αξίζει να αναφερθεί σχετικά με αυτό το παράδειγμα. Η μεταβλητή done ελέγχει αν η partition έχει τελειώσει την κίνηση στα στοιχεία ή όχι. Έτσι όταν μια από τους δύο εσωτερικούς βρόχους θέλει να τελειώσει ολόκληρη την ακολουθία ανταλλαγής, θέτει τη done σε 1 και μετά φεύγει με break. Γιατί οι εσωτερικοί βρόχοι χρησιμοποιούν τη done; Γιατί, όταν ο πρώτος εσωτερικός βρόχος τελειώνει με μια break, το αν ο επόμενος βρόχος θα αρχίσει εξαρτάται από το αν ο κύριος βρόχος έχει τελειώσει, δηλαδή, από το αν η done έχει τεθεί σε 1:

while not done:
    while not done:
        # επαναλαμβάνει μέχρι ένα break

    while not done:
        # εκτελείται μόνο αν ο πρώτος δεν όρισε την done σε 1

Μια ισοδύναμη, ίσως καθαρότερη, αλλά κατά τη γνώμη μου λιγότερο ωραία έκδοση θα ήταν:

while not done:
    while 1:
        # επαναλαμβάνει μέχρι ένα break

    if not done:
        while 1:
            # εκτελείται μόνο αν ο πρώτος δεν όρισε την done σε 1

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

Περισσότερα παραδείγματα μπορούν να βρεθούν στη σελίδα tidbit του Joe Strout.


Copyright © Magnus Lie Hetland
Απόδοση στα ελληνικά: Άγγελος Ορφανάκος <csst0266remove@thiscs.uoi.gr>