9. Κλάσεις
**********

Οι κλάσεις παρέχουν ένα μέσο ομαδοποίησης δεδομένων και
λειτουργικότητας. Η δημιουργία μιας νέας κλάσης δημιουργεί έναν νέο
*τύπο* αντικειμένου, επιτρέποντας νέα *στιγμιότυπα* αυτού του τύπου
που πρόκειται να γίνουν. Κάθε στιγμιότυπο κλάσης μπορεί να έχει
χαρακτηριστικά που συνδέονται με αυτό για τη διατήρηση της κατάστασής
του. Τα στιγμιότυπα κλάσης μπορούν να έχουν επίσης μεθόδους (που
ορίζονται από την κλάση του) για την τροποποίηση της κατάστασής του.

Σε σύγκριση με άλλες γλώσσες προγραμματισμού, ο μηχανισμός κλάσης της
Python προσθέτει κλάσεις με ελάχιστο νέο συντακτικό και σημασιολογία.
Είναι ένα μείγμα των μηχανισμών κλάσεων που βρέθηκαν στη C++ και στο
Modula-3. Οι κλάσεις της Python παρέχουν όλα τα standard
χαρακτηριστικά του Αντικειμενοστραφούς Προγραμματισμού: ο μηχανισμός
της κληρονομικότητας της κλάσης επιτρέπει την ύπαρξη πολλαπλών βασικών
κλάσεων, μια παραγόμενη κλάση να μπορεί να παρακάμψει οποιεσδήποτε
μεθόδους της βασικής κλάσης ή κλάσεων, και μια μέθοδος να μπορεί να
καλέσει τη μέθοδο μίας βασικής κλάσης με το ίδιο όνομα. Τα αντικείμενα
μπορούν να περιέχουν αυθαίρετα ποσά και είδη δεδομένων. Όπως ισχύει
για τα modules, οι κλάσεις συμμετέχουν στη δυναμική φύση της Python:
δημιουργούνται κατά το χρόνο εκτέλεσης και μπορούν να τροποποιηθούν
περαιτέρω μετά τη δημιουργία.

Στην ορολογία της C++, συνήθως τα μέλη της κλάσης (συμπεριλαμβανομένων
των μελών δεδομένων) είναι *δημόσια* (εκτός από βλέπε παρακάτω
Ιδιωτικές Μεταβλητές), και όλες οι συμμετέχουσες συναρτήσεις είναι
*εικονικές*. Όπως και στο Modula-3, δεν υπάρχουν συντομογραφίες για
την αναφορά στα μέλη του αντικειμένου από τις μεθόδους του: η μέθοδος
δηλώνεται με ρητό πρώτο όρισμα που αντιπροσωπεύει το αντικείμενο, το
οποίο παρέχεται έμμεσα από την κλήση. Όπως και στο Smalltalk, οι ίδιες
οι κλάσεις είναι αντικείμενα.Αυτό παρέχει σημασιολογία για εισαγωγή
και μετονομασία. Σε αντίθεση με τις γλώσσες C++ και Modula-3, οι
built-in τύποι μπορούν να χρησιμοποιηθούν ως βασικές κλάσεις για
επέκταση από τον χρήστη. Επίσης, όπως στην C++, οι περισσότεροι built-
in τελεστές με ειδική σύνταξη (αριθμητικοί τελεστές, εγγραφή κ.λπ.)
μπορούν να επαναπροσδιοριστούν για τα στιγμιότυπα κλάσης.

(Ελλείψει καθολικής αποδεκτής ορολογίας για να μιλήσω για τις κλάσεις,
θα κάνω περιστασιακή χρήση όρων από τη Smalltalk και τη C++. Θα
χρησιμοποιούσα όρους  από τη Modula-3, καθώς η αντικειμενοστραφής
σημασιολογία του είναι πιο κοντά σε αυτήν της Python από ότι της C++,
Αλλά πιστεύω ότι λίγοι αναγνώστες το έχουν ακούσει.)


9.1. Λίγα λόγια για Ονόματα και Αντικείμενα
===========================================

Τα αντικείμενα έχουν μοναδικότητα και πολλά ονόματα (σε πολλαπλά
πεδία) μπορούν να συνδεθούν στο ίδιο αντικείμενο. Αυτό είναι γνωστό ως
ψευδώνυμο σε άλλες γλώσσες.Αυτό συνήθως δεν εκτιμάται με μια πρώτη
ματιά στην Python και μπορεί να αγνοείται με ασφάλεια όταν ασχολείται
με αμετάβλητους βασικούς τύπους (αριθμοί, συμβολοσειρές, πλειάδες
(tuples)). Ωστόσο, το ψευδώνυμο έχει μια πιθανώς εκπληκτική επίδραση
στη σημασιολογία του κώδικα της Python που περιλαμβάνει ευμετάβλητα
αντικείμενα όπως λίστες, λεξικά, και τους περισσότερους άλλους τύπους.
Αυτό χρησιμοποιείται συνήθως προς όφελος του προγράμματος, δεδομένου
ότι τα ψευδώνυμα συμπεριφέρονται σαν δείκτες από ορισμένες απόψεις.
Για παράδειγμα, η μετάδοση ενός αντικειμένου είναι ανέξοδη αφού μόνο
ένας δείκτης περνά από την υλοποίηση, και αν μια συνάρτηση τροποποιεί
ένα αντικείμενο που έχει περάσει ως όρισμα, ο καλών θα δει την αλλαγή
--- αυτό εξαλείφει την ανάγκη για δύο διαφορετικούς μηχανισμούς
μετάδοσης ορισμάτων όπως στην Pascal.


9.2. Εμβέλεια και Πεδία Ονομάτων στην Python
============================================

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

Ας ξεκινήσουμε με ορισμένους ορισμούς.

Ένας *πεδίο ονομάτων (namespace)* είναι μια αντιστοίχιση από ονόματα
σε αντικείμενα. Τα περισσότερα πεδία ονομάτων υλοποιούνται επί του
παρόντος ως λεξικά Python, αλλά αυτό συνήθως δεν γίνεται αντιληπτό με
κανέναν τρόπο (εκτός από την απόδοση) και μπορεί να αλλάξει στο
μέλλον. Παραδείγματα πεδίων ονομάτων είναι: το σύνολο των
ενσωματωμένων ονομάτων (που περιέχει συναρτήσεις όπως "abs()" και
ενσωματωμένα ονόματα εξαιρέσεων)∙ τα καθολικά ονόματα σε ένα module
και τα τοπικά ονόματα σε μια επίκληση συνάρτησης. Κατά μία έννοια το
σύνολο των χαρακτηριστικών ενός αντικειμένου σχηματίζει επίσης ένα
πεδίο ονομάτων. Το σημαντικό πράγμα που πρέπει να γνωρίζετε για τα
πεδία ονομάτων είναι ότι δεν υπάρχει καμία απολύτως σχέση μεταξύ
ονομάτων σε διαφορετικά πεδία ονομάτων, για παράδειγμα, δύο
διαφορετικά modules μπορεί και τα δύο να ορίσουν μια συνάρτηση
"maximize" χωρίς σύγχυση --- χρήστες των modules πρέπει να την
προσθέσουν με το όνομα του module.

Παρεμπιπτόντως, χρησιμοποιώ τη λέξη *attribute* για οποιοδήποτε όνομα
που ακολουθεί μια τελεία --- για παράδειγμα, στην έκφραση "z.real", το
"real" είναι ένα attribute του αντικειμένου "z". Αυστηρά μιλώντας, οι
αναφορές σε ονόματα των modules είναι αναφορές σε attributes: στην
έκφραση "modname.funcname", το "modname" είναι ένα module αντικείμενο
και το "funcname" είναι ένα attribute του αντικειμένου. Σε αυτήν την
περίπτωση συμβαίνει να υπάρχει μια απλή αντιστοίχιση μεταξύ των
attributes των modules και των καθολικών ονομάτων που ορίζονται στο
module: μοιράζονται τον ίδιο χώρο ονομάτων! [1]

Attributes may be read-only or writable.  In the latter case,
assignment to attributes is possible.  Module attributes are writable:
you can write "modname.the_answer = 42".  Writable attributes may also
be deleted with the "del" statement.  For example, "del
modname.the_answer" will remove the attribute "the_answer" from the
object named by "modname".

Οι χώροι ονομάτων δημιουργούνται σε διαφορετικές στιγμές και έχουν
διαφορετική διάρκεια ζωής.Ο χώρος ονομάτων που περιέχει τα built-in
ονόματα δημιουργείται κατά την εκκίνηση του διερμηνέα της Python και
δεν διαγράφεται ποτέ. Ο καθολικός χώρος ονομάτων για ένα module
δημιουργείται όταν διαβάζεται ο ορισμός του module. Κανονικά, οι χώροι
ονομάτων των modules διαρκούν επίσης μέχρι να τερματιστεί ο
διερμηνέας. Οι δηλώσεις που εκτελούνται από την επίκληση ανώτατου
επιπέδου του διερμηνέα, είτε διαβάζονται από ένα script είτε
διαδραστικά, θεωρούνται μέρος ενός module που ονομάζεται "__main__",
επομένως έχουν τον δικό τους καθολικό χώρο ονομάτων. (Τα ενσωματωμένα
ονόματα στην πραγματικότητα υπάρχουν επίσης σε ένα module,αυτό
ονομάζεται "builtins".)

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

Η εμβέλεια είναι μια περιοχή κειμένου ενός προγράμματος Python όπου
ένας χώρος ονομάτων είναι άμεσα προσβάσιμος. Το "Άμεση πρόσβαση" εδώ
σημαίνει ότι μια ανεπιφύλακτη αναφορά σε ένα όνομα προσπαθεί να βρει
το όνομα στον χώρο ονομάτων.

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

* η ενδότερη εμβέλεια, η οποία αναζητείται πρώτα, περιέχει τα τοπικά
  ονόματα

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

* η επόμενη προς την τελευταία εμβέλεια περιέχει τα τρέχοντα καθολικά
  ονόματα του module

* η πιο απομακρυσμένη εμβέλεια (που έγινε τελευταία αναζήτηση) είναι ο
  χώρος ονομάτων που περιέχει built-in ονόματα

Εάν ένα όνομα έχει δηλωθεί ως καθολικό, τότε όλες οι αναφορές και οι
εκχωρήσεις πηγαίνουν απευθείας στο επόμενο προς το τελευταίο πεδίο που
περιέχει τα καθολικά ονόματα του module. Για την επανασύνδεση
μεταβλητών που βρίσκονται εκτός της ενδότερης εμβέλειας ,μπορεί να
χρησιμοποιηθεί η δήλωση του "nonlocal" . Εάν δεν δηλωθούν ως μη
τοπικές, αυτές οι μεταβλητές είναι μόνο για ανάγνωση (μια προσπάθεια
εγγραφής σε μια τέτοια μεταβλητή θα δημιουργήσει απλώς μια *νέα*
τοπική μεταβλητή στην ενδότερη εμβέλεια, αφήνοντας αμετάβλητη την
εξωτερική μεταβλητή με το ίδιο όνομα ).

Συνήθως, η τοπική εμβέλεια παραπέμπει στα τοπικά ονόματα της
(κείμενης) τρέχουσας συνάρτησης. Εκτός συναρτήσεων, η τοπική εμβέλεια
αναφέρεται στον ίδιο χώρο ονομάτων με την καθολική εμβέλεια: τον χώρο
ονομάτων του module.Οι ορισμοί κλάσεων τοποθετούν έναν ακόμη χώρο
ονομάτων στην τοπική εμβέλεια.

Είναι σημαντικό να συνειδητοποιήσουμε ότι οι εμβέλειες καθορίζονται με
κείμενο: η καθολική εμβέλεια μιας συνάρτησης που ορίζεται σε ένα
module είναι ο χώρος ονομάτων αυτού του module ανεξάρτητα από το πού ή
με ποιο ψευδώνυμο καλείται η συνάρτηση. Από την άλλη πλευρά, η
πραγματική αναζήτηση ονομάτων γίνεται δυναμικά, κατά το χρόνο
εκτέλεσης --- ωστόσο, ο ορισμός της γλώσσας εξελίσσεται προς τη
στατική ανάλυση ονομάτων, την ώρα της "μεταγλώττισης", επομένως μην
βασίζεστε σε δυναμική ανάλυση ονόματος! (Στην πραγματικότητα, οι
τοπικές μεταβλητές έχουν ήδη καθοριστεί στατικά.)

Μια ιδιαίτερη ιδιορρυθμία της Python είναι ότι -- αν οι δηλώσεις
"global" ή "nonlocal" δεν είναι σε ισχύ -- οι εκχωρήσεις στα ονόματα
πηγαίνουν πάντα στην ενδότερη εμβέλεια. Οι εκχωρήσεις δεν αντιγράφουν
δεδομένα --- απλώς δεσμεύουν ονόματα σε αντικείμενα. Το ίδιο ισχύει
και για τις διαγραφές: η δήλωση *del x* αφαιρεί την σύνδεση του "x"
από τον χώρο ονομάτων που αναφέρεται από την τοπική εμβέλεια. Στην
πραγματικότητα, όλες οι λειτουργίες που εισάγουν νέα ονόματα
χρησιμοποιούν την τοπική εμβέλεια: συγκεκριμένα οι δηλώσεις, "import"
και οι ορισμοί συναρτήσεων δεσμεύουν το όνομα του module ή της
συνάρτησης στην τοπική εμβέλεια.

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


9.2.1. Παράδειγμα Εμβέλειας και Χώρων Ονομάτων
----------------------------------------------

Αυτό είναι ένα παράδειγμα που δείχνει τον τρόπο αναφοράς στα
διαφορετικά πεδία και χώρους ονομάτων και πώς τα "global" και
"nonlocal" επηρεάζουν τα variable binding:

   def scope_test():
       def do_local():
           spam = "local spam"

       def do_nonlocal():
           nonlocal spam
           spam = "nonlocal spam"

       def do_global():
           global spam
           spam = "global spam"

       spam = "test spam"
       do_local()
       print("After local assignment:", spam)
       do_nonlocal()
       print("After nonlocal assignment:", spam)
       do_global()
       print("After global assignment:", spam)

   scope_test()
   print("In global scope:", spam)

Το αποτέλεσμα του κώδικα στο παράδειγμα είναι:

   After local assignment: test spam
   After nonlocal assignment: nonlocal spam
   After global assignment: nonlocal spam
   In global scope: global spam

Σημειώστε πώς η εκχώρηση *τοπική* (η οποία είναι προεπιλεγμένη) δεν
άλλαξε την δέσμευση *scope_test*'s  του *spam*. Η εκχώρηση "nonlocal"
άλλαξε την δέσμευση του *scope_test*'s του *spam* και η εκχώρηση του
"global" άλλαξε τη δέσμευση σε επίπεδο module.

Μπορείτε επίσης να δείτε ότι δεν υπήρχε προηγούμενη δέσμευση για
*spam* πριν από την εκχώρηση "global"..


9.3. Μια πρώτη ματιά στις Κλάσεις
=================================

Οι Κλάσεις εισάγουν λίγη νέα σύνταξη, τρεις νέους τύπους αντικειμένων
και κάποια νέα σημασιολογία.


9.3.1. Σύνταξη Ορισμού Κλάσης
-----------------------------

Η απλούστερη μορφή ορισμού κλάσης μοιάζει με αυτό:

   class ClassName:
       <statement-1>
       .
       .
       .
       <statement-N>

Ορισμοί κλάσεων, όπως ορισμοί συναρτήσεων ("def" δηλώσεις) πρέπει να
εκτελεστούν προτού έχουν οποιοδήποτε αποτέλεσμα. (Θα μπορούσατε να
τοποθετήσετε έναν ορισμό κλάσης σε έναν κλάδο μιας δήλωσης "if" ή μέσα
σε μια συνάρτηση.)

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

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

When a class definition is left normally (via the end), a *class
object* is created.  This is basically a wrapper around the contents
of the namespace created by the class definition; we'll learn more
about class objects in the next section.  The original local scope
(the one in effect just before the class definition was entered) is
reinstated, and the class object is bound here to the class name given
in the class definition header ("ClassName" in the example).


9.3.2. Αντικείμενα Κλάσης
-------------------------

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

Οι *Αναφορές χαρακτηριστικών* χρησιμοποιούν την τυπική σύνταξη που
χρησιμοποιείται για όλα τις αναφορές χαρακτηριστικών στην Python:
"obj.name". Τα έγκυρα ονόματα χαρακτηριστικών είναι όλα τα ονόματα που
βρίσκονταν στον χώρο ονομάτων της κλάσης όταν δημιουργήθηκε το
αντικείμενο της κλάσης. Έτσι, αν ο ορισμός της κλάσης έμοιαζε ως εξής:

   class MyClass:
       """A simple example class"""
       i = 12345

       def f(self):
           return 'hello world'

then "MyClass.i" and "MyClass.f" are valid attribute references,
returning an integer and a function object, respectively. Class
attributes can also be assigned to, so you can change the value of
"MyClass.i" by assignment. "__doc__" is also a valid attribute,
returning the docstring belonging to the class: ""A simple example
class"".

Η κλάση *στιγμιότυπο* χρησιμοποιεί σημειογραφία συνάρτησης. Απλώς
προσποιηθείτε ότι το αντικείμενο της κλάσης είναι μια συνάρτηση χωρίς
παραμέτρους που επιστρέφει ένα νέο στιγμιότυπο της κλάσης.Για
παράδειγμα (υποθέτοντας την παραπάνω κλάση):

   x = MyClass()

δημιουργεί ένα νέο *στιγμιότυπο* της κλάσης και εκχωρεί αυτό το
αντικείμενο στην τοπική μεταβλητή "x".

The instantiation operation ("calling" a class object) creates an
empty object. Many classes like to create objects with instances
customized to a specific initial state. Therefore a class may define a
special method named "__init__()", like this:

   def __init__(self):
       self.data = []

When a class defines an "__init__()" method, class instantiation
automatically invokes "__init__()" for the newly created class
instance.  So in this example, a new, initialized instance can be
obtained by:

   x = MyClass()

Of course, the "__init__()" method may have arguments for greater
flexibility.  In that case, arguments given to the class instantiation
operator are passed on to "__init__()".  For example,

   >>> class Complex:
   ...     def __init__(self, realpart, imagpart):
   ...         self.r = realpart
   ...         self.i = imagpart
   ...
   >>> x = Complex(3.0, -4.5)
   >>> x.r, x.i
   (3.0, -4.5)


9.3.3. Αντικείμενα Στιγμιοτύπων
-------------------------------

Τώρα τι μπορούμε να κάνουμε με τα αντικείμενα στιγμιοτύπων; Οι μόνες
λειτουργίες που γίνονται κατανοητές από τα αντικείμενα στιγμιοτύπων
είναι οι αναφορές χαρακτηριστικών. Υπάρχουν δύο είδη έγκυρων ονομάτων
attributes: attributes και μέθοδοι δεδομένων.

*Data attributes* correspond to "instance variables" in Smalltalk, and
to "data members" in C++.  Data attributes need not be declared; like
local variables, they spring into existence when they are first
assigned to.  For example, if "x" is the instance of "MyClass" created
above, the following piece of code will print the value "16", without
leaving a trace:

   x.counter = 1
   while x.counter < 10:
       x.counter = x.counter * 2
   print(x.counter)
   del x.counter

The other kind of instance attribute reference is a *method*. A method
is a function that "belongs to" an object.  (In Python, the term
method is not unique to class instances: other object types can have
methods as well.  For example, list objects have methods called
append, insert, remove, sort, and so on. However, in the following
discussion, we'll use the term method exclusively to mean methods of
class instance objects, unless explicitly stated otherwise.)

Τα έγκυρα ονόματα μεθόδων ενός αντικειμένου στιγμιότυπου εξαρτώνται
από την κλάση του. Εξ ορισμού, όλα τα χαρακτηριστικά μιας κλάσης που
είναι αντικείμενα συνάρτησης ορίζουν τις αντίστοιχες μεθόδους των
στιγμιοτύπων της. Έτσι στο παράδειγμά μας, το *x.f* είναι μια έγκυρη
αναφορά μεθόδου, αφού το *MyClass.f* είναι συνάρτηση, αλλά το *x.i*
δεν είναι αφού το *MyClass.i* δεν είναι. Αλλά το *x.f* δεν είναι το
ίδιο πράγμα με το *MyClass.f* --- είναι ένα *αντικείμενο μεθόδου*, όχι
ένα αντικείμενο συνάρτησης.


9.3.4. Αντικείμενα Μεθόδου
--------------------------

Συνήθως, μια μέθοδος καλείται αμέσως μετά τη δέσμευσή της:

   x.f()

In the "MyClass" example, this will return the string "'hello world'".
However, it is not necessary to call a method right away: "x.f" is a
method object, and can be stored away and called at a later time.  For
example:

   xf = x.f
   while True:
       print(xf())

θα συνεχίσει να εκτυπώνει το "hello world" μέχρι το τέλος του χρόνου.

What exactly happens when a method is called?  You may have noticed
that "x.f()" was called without an argument above, even though the
function definition for "f()" specified an argument.  What happened to
the argument? Surely Python raises an exception when a function that
requires an argument is called without any --- even if the argument
isn't actually used...

Στην πραγματικότητα, μπορεί να έχετε μαντέψει την απάντηση: το
ιδιαίτερο με τις μεθόδους είναι ότι το αντικείμενο του στιγμιότυπου
μεταβιβάζεται ως το πρώτο όρισμα της συνάρτησης. Στο παράδειγμά μας, η
κλήση "x.f()" είναι ακριβώς ισοδύναμη με το "MyClass.f(x)". Γενικά, η
κλήση μιας μεθόδου με μια λίστα από *n* ορίσματα ισοδυναμεί με την
κλήση της αντίστοιχης συνάρτησης με μια λίστα ορισμάτων που
δημιουργείται με την εισαγωγή του αντικειμένου στιγμιότυπου της
μεθόδου πριν από το πρώτο όρισμα.

If you still don't understand how methods work, a look at the
implementation can perhaps clarify matters.  When a non-data attribute
of an instance is referenced, the instance's class is searched.  If
the name denotes a valid class attribute that is a function object, a
method object is created by packing (pointers to) the instance object
and the function object just found together in an abstract object:
this is the method object.  When the method object is called with an
argument list, a new argument list is constructed from the instance
object and the argument list, and the function object is called with
this new argument list.


9.3.5. Μεταβλητές Κλάσης και Στιγμιότυπου
-----------------------------------------

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

   class Dog:

       kind = 'canine'         # class variable shared by all instances

       def __init__(self, name):
           self.name = name    # instance variable unique to each instance

   >>> d = Dog('Fido')
   >>> e = Dog('Buddy')
   >>> d.kind                  # shared by all dogs
   'canine'
   >>> e.kind                  # shared by all dogs
   'canine'
   >>> d.name                  # unique to d
   'Fido'
   >>> e.name                  # unique to e
   'Buddy'

Όπως συζητήθηκε στο Λίγα λόγια για Ονόματα και Αντικείμενα, τα κοινά
δεδομένα μπορεί να έχουν πιθανώς εκπληκτικά αποτελέσματα με τη
συμμετοχή αντικειμένων *mutable*  όπως λίστες και λεξικά. Για
παράδειγμα, η λίστα *tricks* στον παρακάτω κώδικα δεν θα πρέπει να
χρησιμοποιείται ως μεταβλητή κλάσης επειδή μόνο μία λίστα θα μπορούσε
να είναι κοινή σε όλα τα στιγμιότυπα *Dog*:

   class Dog:

       tricks = []             # mistaken use of a class variable

       def __init__(self, name):
           self.name = name

       def add_trick(self, trick):
           self.tricks.append(trick)

   >>> d = Dog('Fido')
   >>> e = Dog('Buddy')
   >>> d.add_trick('roll over')
   >>> e.add_trick('play dead')
   >>> d.tricks                # unexpectedly shared by all dogs
   ['roll over', 'play dead']

Ο σωστός σχεδιασμός της κλάσης θα πρέπει να χρησιμοποιεί μια μεταβλητή
στιγμιότυπου αντί:

   class Dog:

       def __init__(self, name):
           self.name = name
           self.tricks = []    # creates a new empty list for each dog

       def add_trick(self, trick):
           self.tricks.append(trick)

   >>> d = Dog('Fido')
   >>> e = Dog('Buddy')
   >>> d.add_trick('roll over')
   >>> e.add_trick('play dead')
   >>> d.tricks
   ['roll over']
   >>> e.tricks
   ['play dead']


9.4. Τυχαίες Παρατηρήσεις
=========================

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

   >>> class Warehouse:
   ...    purpose = 'storage'
   ...    region = 'west'
   ...
   >>> w1 = Warehouse()
   >>> print(w1.purpose, w1.region)
   storage west
   >>> w2 = Warehouse()
   >>> w2.region = 'east'
   >>> print(w2.purpose, w2.region)
   storage east

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

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

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

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

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

   # Function defined outside the class
   def f1(self, x, y):
       return min(x, x+y)

   class C:
       f = f1

       def g(self):
           return 'hello world'

       h = g

Now "f", "g" and "h" are all attributes of class "C" that refer to
function objects, and consequently they are all methods of instances
of "C" --- "h" being exactly equivalent to "g".  Note that this
practice usually only serves to confuse the reader of a program.

Οι μέθοδοι μπορούν να καλούν άλλες μεθόδους χρησιμοποιώντας
χαρακτηριστικά μεθόδου του argument "self":

   class Bag:
       def __init__(self):
           self.data = []

       def add(self, x):
           self.data.append(x)

       def addtwice(self, x):
           self.add(x)
           self.add(x)

Οι μέθοδοι μπορεί να αναφέρονται σε καθολικά ονόματα με τον ίδιο τρόπο
όπως οι συνηθισμένες συναρτήσεις.Η καθολική εμβέλεια που σχετίζεται με
μια μέθοδο είναι το module που περιέχει τον ορισμό της. (Μια κλάση δεν
χρησιμοποιείται ποτέ ως καθολική εμβέλεια.) Αν και σπάνια συναντά
κανείς έναν καλό λόγο για τη χρήση καθολικών δεδομένων σε μια μέθοδο,
υπάρχουν πολλές Νόμιμες χρήσεις της καθολικής εμβέλειας: για ένα
πράγμα, οι λειτουργίες και οι λειτουργικές μονάδες που εισάγονται στην
καθολική εμβέλεια μπορούν να χρησιμοποιηθούν από μεθόδους, καθώς και
συναρτήσεις και κλάσεις που ορίζονται σε αυτό. Συνήθως, η κλάση που
περιέχει τη μέθοδο ορίζεται από μόνη της σε αυτή την καθολική
εμβέλεια, και στην επόμενη ενότητα θα βρούμε μερικούς καλούς λόγους
για τους οποίους μια μέθοδος θα ήθελε να αναφέρει τη δική της κλάση.

Κάθε τιμή είναι ένα αντικείμενο και επομένως έχει μια *κλάση*
(ονομάζεται επίσης *τύπος* της). Αποθηκεύεται ως "object.__class__".


9.5. Κληρονομικότητα
====================

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

   class DerivedClassName(BaseClassName):
       <statement-1>
       .
       .
       .
       <statement-N>

The name "BaseClassName" must be defined in a scope containing the
derived class definition.  In place of a base class name, other
arbitrary expressions are also allowed.  This can be useful, for
example, when the base class is defined in another module:

   class DerivedClassName(modname.BaseClassName):

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

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

Οι παράγωγες κλάσεις ενδέχεται να παρακάμπτουν τις μεθόδους των
βασικών τους κλάσεων. Επειδή οι μέθοδοι δεν έχουν ειδικά προνόμια όταν
καλούν άλλες μεθόδους του ίδιου αντικειμένου, μια μέθοδος μιας βασικής
κλάσης που καλεί μια άλλη μέθοδο που ορίζεται στην ίδια βασική κλάση
μπορεί να καταλήξει να καλεί μια μέθοδο μιας παραγόμενης κλάσης που
την αντικαθιστά.(Για προγραμματιστές C++: όλες οι μέθοδοι στην Python
είναι ουσιαστικά «εικονικές».)

Μια υπερισχύουσα μέθοδος σε μια παραγόμενη κλάση μπορεί στην
πραγματικότητα να θέλει να επεκτείνει αντί να αντικαταστήσει απλώς τη
μέθοδο βασικής κλάσης με το ίδιο όνομα. Υπάρχει ένας απλός τρόπος για
να καλέσετε τη μέθοδο βασικής κλάσης απευθείας: απλώς καλέστε το
"BaseClassName.methodname(self, arguments)`. Αυτό είναι περιστασιακά
χρήσιμο στους χρήστες (Λάβετε υπόψη ότι αυτό λειτουργεί μόνο εάν η
βασική κλάση είναι προσβάσιμη ως ``BaseClassName" στην καθολική
εμβέλεια.)

Η Python έχει δύο (ενσωματωμένες) built-in συναρτήσεις που λειτουργούν
με κληρονομικότητα:

* Χρησιμοποιήστε το "isinstance()" για να ελέγξετε τον τύπο ενός
  στιγμιότυπου: το "isinstance(obj, int)" θα είναι "True" μόνο εάν το
  "obj.__class__" είναι "int" ή προέρχεται από κάποια κλάση από "int".

* Χρησιμοποιήστε το "issubclass()" για να ελέγξετε την κληρονομικότητα
  κλάσης: Το "issubclass(bool, int)" είναι `` True`` αφού το "bool"
  είναι υποκλάση του "int". Ωστόσο, το "issubclass(float, int)" είναι
  "False" αφού το "float" δεν είναι υποκλάση του "int".


9.5.1. Πολλαπλή Κληρονομικότητα
-------------------------------

Η Python υποστηρίζει επίσης μια μορφή πολλαπλής κληρονομικότητας. Ένας
ορισμός κλάσης με πολλαπλές βασικές κλάσεις μοιάζει με αυτό:

   class DerivedClassName(Base1, Base2, Base3):
       <statement-1>
       .
       .
       .
       <statement-N>

For most purposes, in the simplest cases, you can think of the search
for attributes inherited from a parent class as depth-first, left-to-
right, not searching twice in the same class where there is an overlap
in the hierarchy. Thus, if an attribute is not found in
"DerivedClassName", it is searched for in "Base1", then (recursively)
in the base classes of "Base1", and if it was not found there, it was
searched for in "Base2", and so on.

Στην πραγματικότητα, είναι λίγο πιο περίπλοκο από αυτό. Η σειρά
ανάλυσης της μεθόδου αλλάζει δυναμικά για να υποστηρίξει συνεργατικές
κλήσεις σε "super()". Αυτή η προσέγγιση είναι γνωστή σε ορισμένες
άλλες γλώσσες πολλαπλής κληρονομικότητας ως call-next-method και είναι
πιο ισχυρή από τη σούπερ κλήση που βρίσκεται σε γλώσσες μεμονωμένης
κληρονομικότητας.

Dynamic ordering is necessary because all cases of multiple
inheritance exhibit one or more diamond relationships (where at least
one of the parent classes can be accessed through multiple paths from
the bottommost class).  For example, all classes inherit from
"object", so any case of multiple inheritance provides more than one
path to reach "object".  To keep the base classes from being accessed
more than once, the dynamic algorithm linearizes the search order in a
way that preserves the left-to-right ordering specified in each class,
that calls each parent only once, and that is monotonic (meaning that
a class can be subclassed without affecting the precedence order of
its parents). Taken together, these properties make it possible to
design reliable and extensible classes with multiple inheritance.  For
more detail, see https://www.python.org/download/releases/2.3/mro/.


9.6. Ιδιωτικές Μεταβλητές
=========================

Οι μεταβλητές στιγμιοτύπου "Private" στις οποίες δεν είναι δυνατή η
πρόσβαση εκτός από το εσωτερικό ενός αντικειμένου,δεν υπάρχουν στην
Python. Ωστόσο, υπάρχει μια σύμβαση που ακολουθείται από τον
περισσότερο Python κώδικα: ένα όνομα με πρόθεμα κάτω παύλα (π.χ.
"_spam") θα πρέπει να αντιμετωπίζεται ως μη δημόσιο μέρος του API
(είτε πρόκειται για συνάρτηση,μέθοδο ή μέλος δεδομένων). Θα πρέπει να
θεωρείται ως λεπτομέρεια υλοποίησης και υπόκειται σε αλλαγές χωρίς
προειδοποίηση.

Δεδομένου ότι υπάρχει μια έγκυρη περίπτωση χρήσης για ιδιωτικά μέλη
της κλάσης (δηλαδή για να αποφευχθούν συγκρούσεις ονομάτων με ονόματα
που ορίζονται από υποκλάσεις), υπάρχει περιορισμένη υποστήριξη για
έναν τέτοιο μηχανισμό, που ονομάζεται *name mangling*. Οποιοδήποτε
αναγνωριστικό της φόρμας "__spam" (τουλάχιστον δύο προπορευόμενες κάτω
παύλες, το πολύ μια στη συνέχεια κάτω παύλα) αντικαθίσταται με κείμενο
με το "_classname__spam", όπου το "classname" είναι το όνομα της
τρέχουσας τάξης με την πρώτη υπογράμμιση stripped. Αυτό το mangling
γίνεται χωρίς να λαμβάνεται υπόψη η συντακτική θέση του του
αναγνωριστικού, αρκεί να εμφανίζεται εντός του ορισμού μιας κλάσης.

Η παραβίαση ονομάτων είναι χρήσιμη για να επιτρέπεται στις υποκλάσεις
να παρακάμπτουν μεθόδους χωρίς να διακόπτουν τις κλήσεις μεθόδων
ενδοκλάσεων. Για παράδειγμα:

   class Mapping:
       def __init__(self, iterable):
           self.items_list = []
           self.__update(iterable)

       def update(self, iterable):
           for item in iterable:
               self.items_list.append(item)

       __update = update   # private copy of original update() method

   class MappingSubclass(Mapping):

       def update(self, keys, values):
           # provides new signature for update()
           # but does not break __init__()
           for item in zip(keys, values):
               self.items_list.append(item)

Το παραπάνω παράδειγμα θα λειτουργούσε ακόμα και αν το
"MappingSubclass" εισήγαγε ένα αναγνωριστικό "__update" αφού
αντικαταστάθηκε με το "_Mapping__update" στην κλάση "Mapping" και με
το "_MappingSubclass__update" στη κλάση "MappingSubclass" αντίστοιχα.

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

Σημειώστε ότι ο κώδικας που μεταβιβάστηκε στο "exec()" ή στο "eval()"
δεν θεωρεί ότι το το όνομα κλάσης της κλάσης επίκλησης να είναι η
τρέχουσα κλάση. Αυτό είναι παρόμοιο με το αποτέλεσμα της "καθολικής"
δήλωσης , το αποτέλεσμα της οποίας επίσης περιορίζεται στον κώδικα που
έχει μεταγλωττιστεί μαζί(byte-compiled). Ο ίδιος περιορισμός ισχύει
για τα "getattr()", "setattr()" και "delattr()", καθώς και όταν
γίνεται αναφορά απευθείας στο "__dict__".


9.7. Μικροπράγματα
==================

Μερικές φορές είναι χρήσιμο να έχετε έναν τύπο δεδομένων παρόμοιο με
τον Pascal "record" ή C "struct", ομαδοποιώντας μερικά επώνυμα
στοιχεία δεδομένων. Η ιδιωματική προσέγγιση είναι η χρήση
"dataclasses" για αυτόν τον σκοπό:

   from dataclasses import dataclass

   @dataclass
   class Employee:
       name: str
       dept: str
       salary: int

   >>> john = Employee('john', 'computer lab', 1000)
   >>> john.dept
   'computer lab'
   >>> john.salary
   1000

A piece of Python code that expects a particular abstract data type
can often be passed a class that emulates the methods of that data
type instead.  For instance, if you have a function that formats some
data from a file object, you can define a class with methods "read()"
and "readline()" that get the data from a string buffer instead, and
pass it as an argument.

Instance method objects have attributes, too: "m.__self__" is the
instance object with the method "m()", and "m.__func__" is the
function object corresponding to the method.


9.8. Επαναλήπτες
================

Μέχρι τώρα πιθανότατα έχετε παρατηρήσει ότι τα περισσότερα αντικείμενα
container μπορούν να επαναληφθούν χρησιμοποιώντας μια δήλωση "for":

   for element in [1, 2, 3]:
       print(element)
   for element in (1, 2, 3):
       print(element)
   for key in {'one':1, 'two':2}:
       print(key)
   for char in "123":
       print(char)
   for line in open("myfile.txt"):
       print(line, end='')

Αυτό το στυλ πρόσβασης είναι σαφές, συνοπτικό και βολικό. Η χρήση των
Iterators διαπερνά και ενοποιεί την Python. Στο παρασκήνιο, η δήλωση
"for" καλεί "iter()" στο αντικείμενο container. Η συνάρτηση επιστρέφει
ένα αντικείμενο iterator που ορίζει τη μέθοδο "__next__()" η οποία
έχει πρόσβαση σε στοιχεία στο container ένα κάθε φορά. Όταν δεν
υπάρχουν άλλα στοιχεία, το "__next__()" δημιουργεί μια "StopIteration"
εξαίρεση που λέει τον βρόχο "for" να τερματιστεί. Μπορείτε να καλέσετε
τη μέθοδο "__next__()" χρησιμοποιώντας την ενσωματωμένη συνάρτηση
"next()". Αυτό το παράδειγμα δείχνει πώς λειτουργούν όλα:

   >>> s = 'abc'
   >>> it = iter(s)
   >>> it
   <str_iterator object at 0x10c90e650>
   >>> next(it)
   'a'
   >>> next(it)
   'b'
   >>> next(it)
   'c'
   >>> next(it)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
       next(it)
   StopIteration

Having seen the mechanics behind the iterator protocol, it is easy to
add iterator behavior to your classes.  Define an "__iter__()" method
which returns an object with a "__next__()" method.  If the class
defines "__next__()", then "__iter__()" can just return "self":

   class Reverse:
       """Iterator for looping over a sequence backwards."""
       def __init__(self, data):
           self.data = data
           self.index = len(data)

       def __iter__(self):
           return self

       def __next__(self):
           if self.index == 0:
               raise StopIteration
           self.index = self.index - 1
           return self.data[self.index]

   >>> rev = Reverse('spam')
   >>> iter(rev)
   <__main__.Reverse object at 0x00A1DB50>
   >>> for char in rev:
   ...     print(char)
   ...
   m
   a
   p
   s


9.9. Γεννήτορες (Generators)
============================

*Generators* είναι ένα απλό και ισχυρό εργαλείο για τη δημιουργία
iterators. Είναι γραμμένες σαν κανονικές συναρτήσεις αλλά
χρησιμοποιούν τη "yield" όποτε θέλουν να επιστρέψουν δεδομένα. Κάθε
φορά που καλείται "next()" σε αυτό, ο generator συνεχίζει από εκεί που
σταμάτησε (θυμάται όλες τις τιμές δεδομένων και ποια δήλωση
εκτελέστηκε τελευταία). Ένα παράδειγμα δείχνει ότι οι generators
μπορεί να είναι ασήμαντα εύκολο να δημιουργηθούν:

   def reverse(data):
       for index in range(len(data)-1, -1, -1):
           yield data[index]

   >>> for char in reverse('golf'):
   ...     print(char)
   ...
   f
   l
   o
   g

Anything that can be done with generators can also be done with class-
based iterators as described in the previous section.  What makes
generators so compact is that the "__iter__()" and "__next__()"
methods are created automatically.

Ένα άλλο βασικό χαρακτηριστικό είναι ότι οι τοπικές μεταβλητές και η
κατάσταση εκτέλεσης αποθηκεύονται αυτόματα μεταξύ των κλήσεων. Αυτό
έκανε τη συνάρτηση πιο εύκολη στην γραφή και πολύ πιο ξεκάθαρη από μια
προσέγγιση που χρησιμοποιεί μεταβλητές παράδειγμα όπως "self.index"
και "self.data".

Εκτός από την αυτόματη δημιουργία μεθόδου και την αποθήκευση της
κατάστασης του προγράμματος, όταν οι generators τερματίζονται,
εγείρουν αυτόματα την εξαίρεση "StopIteration". Σε συνδυασμό, αυτά τα
χαρακτηριστικά καθιστούν εύκολη τη δημιουργία επαναλήψεων χωρίς
περισσότερη προσπάθεια από τη σύνταξη μιας κανονικής συνάρτησης.


9.10. Εκφράσεις Γεννήτορων
==========================

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

Παραδείγματα:

   >>> sum(i*i for i in range(10))                 # sum of squares
   285

   >>> xvec = [10, 20, 30]
   >>> yvec = [7, 5, 3]
   >>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
   260

   >>> unique_words = set(word for line in page  for word in line.split())

   >>> valedictorian = max((student.gpa, student.name) for student in graduates)

   >>> data = 'golf'
   >>> list(data[i] for i in range(len(data)-1, -1, -1))
   ['f', 'l', 'o', 'g']

-[ Υποσημειώσεις ]-

[1] Except for one thing.  Module objects have a secret read-only
    attribute called "__dict__" which returns the dictionary used to
    implement the module's namespace; the name "__dict__" is an
    attribute but not a global name. Obviously, using this violates
    the abstraction of namespace implementation, and should be
    restricted to things like post-mortem debuggers.
