5. Δομές Δεδομένων
******************

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


5.1. Περισσότερα για τις Λίστες
===============================

Ο τύπος δεδομένων λίστας έχει μερικές ακόμη μεθόδους.  Ακολουθούν όλες
οι μέθοδοι αντικειμένων τύπου λίστας:

list.append(x)

   Προσθέτει ένα στοιχείο στο τέλος της λίστας.  Παρόμοιο με
   "a[len(a):] = [x]".

list.extend(iterable)

   Επεκτείνει τη λίστα προσθέτοντας όλα τα στοιχεία από το iterable.
   Παρόμοιο με "a[len(a):] = iterable".

list.insert(i, x)

   Εισάγει ένα στοιχείο σε μια δεδομένη θέση.  Το πρώτο όρισμα είναι
   το index του στοιχείου πριν από το οποίο θα εισαχθεί, επομένως
   "a.insert(0, x)" εισάγεται στο μπροστινό μέρος της λίστας, και το
   "a.insert(len(a), x)" ισοδυναμεί με "a.append(x)".

list.remove(x)

   Καταργεί το πρώτο στοιχείο από τη λίστα του οποίου η τιμή είναι ίση
   με *x*.  Κάνει raise ένα "ValueError" εάν δεν υπάρχει τέτοιο
   στοιχείο.

list.pop([i])

   Καταργεί το στοιχείο στη δεδομένη θέση στη λίστα, και το
   επιστρέφει.  Εάν δεν έχει καθοριστεί ευρετήριο, το "a.pop()"
   αφαιρεί και επιστρέφει το τελευταίο στοιχείο στη λίστα. Κάνει raise
   ένα "IndexError" εάν η λίστα είναι κενή ή το ευρετήριο βρίσκεται
   εκτός του εύρους της λίστας.

list.clear()

   Αφαιρεί όλα τα στοιχεία από τη λίστα.  Παρόμοιο με "del a[:]".

list.index(x[, start[, end]])

   Return zero-based index of the first occurrence of *x* in the list.
   Raises a "ValueError" if there is no such item.

   Τα προαιρετικά ορίσματα *start* και *end* ερμηνεύονται όπως στη
   σημειογραφία slice και χρησιμοποιούνται για τον περιορισμό της
   αναζήτησης σε μια συγκεκριμένη υποακολουθία της λίστας.  Ο
   επιστρεφόμενος δείκτης υπολογίζεται σε σχέση με την αρχή της
   πλήρους ακολουθίας αντί για το όρισμα *start*.

list.count(x)

   Επιστρέφει τον αριθμό των φορών που εμφανίζεται το *x* στη λίστα.

list.sort(*, key=None, reverse=False)

   Ταξινομεί τα στοιχεία της λίστας στη θέση τους (τα ορίσματα μπορούν
   να χρησιμοποιηθούν για προσαρμογή ταξινόμησης, βλ. "sorted()" για
   την εξήγησή τους).

list.reverse()

   Αντιστρέφει τα στοιχεία της λίστας στη θέση τους.

list.copy()

   Επιστρέφει ένα shallow (ρηχό) αντίγραφο της λίστας. Παρόμοιο με
   "a[:]".

Ένα παράδειγμα που χρησιμοποιεί τις περισσότερες από τις μεθόδους της
λίστας:

   >>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
   >>> fruits.count('apple')
   2
   >>> fruits.count('tangerine')
   0
   >>> fruits.index('banana')
   3
   >>> fruits.index('banana', 4)  # Find next banana starting at position 4
   6
   >>> fruits.reverse()
   >>> fruits
   ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
   >>> fruits.append('grape')
   >>> fruits
   ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
   >>> fruits.sort()
   >>> fruits
   ['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
   >>> fruits.pop()
   'pear'

Μπορεί να έχετε παρατηρήσει ότι μέθοδοι όπως "insert", "remove" or
"sort" που τροποποιούν μόνο τη λίστα δεν έχουν εκτυπωμένη τιμή
επιστροφής -- επιστρέφουν το προεπιλεγμένο (default) "None". [1]  Αυτή
είναι μια αρχή σχεδιασμού για όλες τις μεταβλητές δομές δεδομένων στην
Python.

Ένα άλλο πράγμα που μπορεί να παρατηρήσετε είναι ότι δεν μπορούν να
ταξινομηθούν ή να συγκριθούν όλα τα δεδομένα.  Για παράδειγμα, "[None,
'hello', 10]" δεν ταξινομούνται γιατί ακέραιοι αριθμοί δεν μπορούν να
συγκριθούν με συμβολοσειρές (strings) και το "None" δεν μπορεί να
συγκριθεί με άλλους τύπους.  Επίσης, υπάρχουν ορισμένοι τύποι που δεν
έχουν καθορισμένη σχέση διάταξης.  Για παράδειγμα, το "3+4j < 5+7j"
δεν είναι έγκυρη σύγκριση.


5.1.1. Χρήστη Λιστών ως Στοίβες (Stacks)
----------------------------------------

The list methods make it very easy to use a list as a stack, where the
last element added is the first element retrieved ("last-in, first-
out").  To add an item to the top of the stack, use "append()".  To
retrieve an item from the top of the stack, use "pop()" without an
explicit index.  For example:

   >>> stack = [3, 4, 5]
   >>> stack.append(6)
   >>> stack.append(7)
   >>> stack
   [3, 4, 5, 6, 7]
   >>> stack.pop()
   7
   >>> stack
   [3, 4, 5, 6]
   >>> stack.pop()
   6
   >>> stack.pop()
   5
   >>> stack
   [3, 4]


5.1.2. Χρήση λιστών ως Ουρές (Queues)
-------------------------------------

Είναι επίσης δυνατό να χρησιμοποιηθεί μια λίστα ως ουρά (queue), όπου
το πρώτο στοιχείο που προστίθεται είναι το πρώτο στοιχείο που
ανακτάται ("first-in, first-out") ∙ ωστόσο, οι λίστες δεν είναι
αποτελεσματικές για αυτόν τον σκοπό.  Ενώ το να προσθέσεις και να
αφαιρέσεις (στοιχεία) στο τέλος της λίστας είναι γρήγορο, κάνοντας
αυτές τις προσθέσεις και τις αφαιρέσεις (στοιχείων) στην αρχή της
λίστα είναι αργό (επειδή όλα τα στοιχεία πρέπει να μετατοπιστούν κατά
ένα).

Για να εφαρμόσετε μια ουρά (queue), χρησιμοποιήστε την
"collections.deque" η οποία σχεδιάστηκε για να έχει γρήγορες
προσθέσεις και αφαιρέσεις και από τα δύο άκρα.  Για παράδειγμα:΅:

   >>> from collections import deque
   >>> queue = deque(["Eric", "John", "Michael"])
   >>> queue.append("Terry")           # Terry arrives
   >>> queue.append("Graham")          # Graham arrives
   >>> queue.popleft()                 # The first to arrive now leaves
   'Eric'
   >>> queue.popleft()                 # The second to arrive now leaves
   'John'
   >>> queue                           # Remaining queue in order of arrival
   deque(['Michael', 'Terry', 'Graham'])


5.1.3. Comprehensions Λίστας
----------------------------

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

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

   >>> squares = []
   >>> for x in range(10):
   ...     squares.append(x**2)
   ...
   >>> squares
   [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Λάβετε υπόψη ότι αυτό δημιουργεί (ή αντικαθιστά) μια μεταβλητή με το
όνομα "x" που εξακολουθεί να υπάρχει μετά την ολοκλήρωση της loop.
Μπορούμε να υπολογίσουμε τη λίστα των τετραγώνων χωρίς παρενέργειες
χρησιμοποιώντας:

   squares = list(map(lambda x: x**2, range(10)))

ή, ισοδύναμα:

   squares = [x**2 for x in range(10)]

που είναι πιο συνοπτικό και ευανάγνωστο.

Ένα comprehension λίστας αποτελείται από αγκύλες που περιέχουν μια
έκφραση ακολουθούμενη από μια πρόταση "for", στη συνέχεια μηδέν ή
περισσότερες προτάσεις "for" ή "if".  Το αποτέλεσμα θα είναι μια νέα
λίστα που προκύπτει από την αξιολόγηση της έκφρασης στο πλαίσιο των
προτάσεων "for" και "if" που την ακολουθούν. Για παράδειγμα, αυτή η
λίστα συνδυάζει τα στοιχεία δύο λιστών εάν δεν είναι ίσες:

   >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
   [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

και ισοδυναμεί με:

   >>> combs = []
   >>> for x in [1,2,3]:
   ...     for y in [3,1,4]:
   ...         if x != y:
   ...             combs.append((x, y))
   ...
   >>> combs
   [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Σημειώστε πώς η σειρά των δηλώσεων "for" και "if" είναι ίδια και στα
δύο αποσπάσματα.

Εάν η έκφραση είναι πλειάδα (π.χ. το "(x, y)" στο προηγούμενο
παράδειγμα), πρέπει να μπει σε παρένθεση.

   >>> vec = [-4, -2, 0, 2, 4]
   >>> # create a new list with the values doubled
   >>> [x*2 for x in vec]
   [-8, -4, 0, 4, 8]
   >>> # filter the list to exclude negative numbers
   >>> [x for x in vec if x >= 0]
   [0, 2, 4]
   >>> # apply a function to all the elements
   >>> [abs(x) for x in vec]
   [4, 2, 0, 2, 4]
   >>> # call a method on each element
   >>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
   >>> [weapon.strip() for weapon in freshfruit]
   ['banana', 'loganberry', 'passion fruit']
   >>> # create a list of 2-tuples like (number, square)
   >>> [(x, x**2) for x in range(6)]
   [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
   >>> # the tuple must be parenthesized, otherwise an error is raised
   >>> [x, x**2 for x in range(6)]
     File "<stdin>", line 1
       [x, x**2 for x in range(6)]
        ^^^^^^^
   SyntaxError: did you forget parentheses around the comprehension target?
   >>> # flatten a list using a listcomp with two 'for'
   >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
   >>> [num for elem in vec for num in elem]
   [1, 2, 3, 4, 5, 6, 7, 8, 9]

Τα comprehensions λίστας μπορεί να περιέχουν σύνθετες εκφράσεις και
ένθετες συναρτήσεις:

   >>> from math import pi
   >>> [str(round(pi, i)) for i in range(1, 6)]
   ['3.1', '3.14', '3.142', '3.1416', '3.14159']


5.1.4. Comprehensions Ένθετων Λιστών
------------------------------------

Η αρχική έκφραση σε ένα comprehension λίστας μπορεί να είναι
οποιαδήποτε αυθαίρετη έκφραση, συμπεριλαμβανομένης ενός άλλου
comprehension λίστας.

Σκεφτείτε το ακόλουθο παράδειγμα μιας μήτρας 3x4 που υλοποιήθηκε ως
μια λίστα 3 λιστών μήκους 4:

   >>> matrix = [
   ...     [1, 2, 3, 4],
   ...     [5, 6, 7, 8],
   ...     [9, 10, 11, 12],
   ... ]

Το ακόλουθο comprehension λίστας θα μεταφέρει γραμμές και στήλες:

   >>> [[row[i] for row in matrix] for i in range(4)]
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Όπως είδαμε στην προηγούμενη ενότητα, το comprehension της εσωτερικής
λίστας αξιολογείται στο πλαίσιο του "for" που την ακολουθεί, επομένως
αυτό το παράδειγμα είναι ισοδύναμο με:

   >>> transposed = []
   >>> for i in range(4):
   ...     transposed.append([row[i] for row in matrix])
   ...
   >>> transposed
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

το οποίο, με τη σειρά του, είναι το ίδιο με:

   >>> transposed = []
   >>> for i in range(4):
   ...     # the following 3 lines implement the nested listcomp
   ...     transposed_row = []
   ...     for row in matrix:
   ...         transposed_row.append(row[i])
   ...     transposed.append(transposed_row)
   ...
   >>> transposed
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Στον πραγματικό κόσμο, θα πρέπει να προτιμάτε τις ενσωματωμένες
(built-in) συναρτήσεις από τις σύνθετες εντολές ροής. Η συνάρτηση
"zip()" θα έκανε εξαιρετική δουλειά για αυτήν την περίπτωση χρήσης:

   >>> list(zip(*matrix))
   [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

Δείτε Unpacking Λίστες Ορισμάτων για λεπτομέρειες σχετικά με τον
αστερίσκο σε αυτήν τη γραμμή.


5.2. Η δήλωση "del"
===================

There is a way to remove an item from a list given its index instead
of its value: the "del" statement.  This differs from the "pop()"
method which returns a value.  The "del" statement can also be used to
remove slices from a list or clear the entire list (which we did
earlier by assignment of an empty list to the slice).  For example:

   >>> a = [-1, 1, 66.25, 333, 333, 1234.5]
   >>> del a[0]
   >>> a
   [1, 66.25, 333, 333, 1234.5]
   >>> del a[2:4]
   >>> a
   [1, 66.25, 1234.5]
   >>> del a[:]
   >>> a
   []

Το "del" μπορεί επίσης να χρησιμοποιηθεί για τη διαγραφή ολόκληρων
μεταβλητών:

   >>> del a

Η αναφορά στο όνομα "a" στο εξής είναι ένα σφάλμα (τουλάχιστον μέχρι
να του εκχωρηθεί άλλη τιμή). Θα βρούμε άλλες χρήσεις για το "del"
αργότερα.


5.3. Πλειάδες (Tuples) και Ακολουθίες
=====================================

Είδαμε ότι οι λίστες και οι συμβολοσειρές (strings) έχουνε πολλές
κοινές ιδιότητες, όπως λειτουργίες indexing και slicing.  Είναι δύο
παραδείγματα τύπων δεδομένων *sequence* (δείτε Τύποι Ακολουθίας
(Sequence) --- list, tuple, range).  Δεδομένου ότι η Python είναι μια
εξελισσόμενη γλώσσα, άλλοι τύποι δεδομένων ακολουθίας μπορούν να
προστεθούν.  Υπάρχει επίσης ένας άλλος τυπικός τύπος δεδομένων
ακολουθίας type: the *πλειάδα* (*tuple*).

Μια πλειάδα (tuple) αποτελείται από έναν αριθμό τιμών που χωρίζονται
με κόμματα, για παράδειγμα:

   >>> t = 12345, 54321, 'hello!'
   >>> t[0]
   12345
   >>> t
   (12345, 54321, 'hello!')
   >>> # Tuples may be nested:
   >>> u = t, (1, 2, 3, 4, 5)
   >>> u
   ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
   >>> # Tuples are immutable:
   >>> t[0] = 88888
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: 'tuple' object does not support item assignment
   >>> # but they can contain mutable objects:
   >>> v = ([1, 2, 3], [3, 2, 1])
   >>> v
   ([1, 2, 3], [3, 2, 1])

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

Αν και οι πλειάδες (tuples) μπορεί να φαίνονται παρόμοιες με λίστες,
χρησιμοποιούνται συχνά σε διαφορετικές καταστάσεις και για
διαφορετικούς σκοπούς. Οι πλειάδες (tuples) είναι *immutable*, και
συνήθως περιέχουν μια ετερογενή ακολουθία στοιχείων στα οποία η
πρόσβαση γίνεται μέσω unpacking (δείτε παρακάτω σε αυτήν την ενότητα)
ή το indexing (ή ακόμα και κατά χαρακτηριστικό στην περίπτωση
"namedtuples"). Οι λίστες είναι *mutable*, και τα στοιχεία τους είναι
συνήθως ομοιογενή και προσπελάζονται με επανάληψη στη λίστα.

Ένα ειδικό πρόβλημα είναι η κατασκευή πλειάδων (tuples) που περιέχουν
0 ή 1 στοιχεία: η σύνταξη έχει κάποιες επιπλέον ιδιορρυθμίες για να
τις προσαρμόσει.  Οι κενές πλειάδες κατασκευάζονται από ένα κενό
ζευγάρι παρενθέσεων, μια πλειάδα (tuple) με ένα στοιχείο δημιουργείται
ακολουθώντας μια τιμή με κόμμα (δεν αρκεί να περικλείεται μια μόνο
τιμή σε παρενθέσεις). Άσχημο, αλλά αποτελεσματικό.  Για παράδειγμα:

   >>> empty = ()
   >>> singleton = 'hello',    # <-- note trailing comma
   >>> len(empty)
   0
   >>> len(singleton)
   1
   >>> singleton
   ('hello',)

Η δήλωση "t = 12345, 54321, 'hello!'" είναι ένα παράδειγμα *tuple
packing*: οι τιμές "12345", "54321" και "'hello!'" είναι συσκευασμένες
μαζί σε μια πλειάδα (tuple). Η αντίστροφη λειτουργία είναι επίσης
εφικτή:

   >>> x, y, z = t

Αυτό ονομάζεται, αρκετά σωστά, *sequence unpacking* και λειτουργεί για
οποιαδήποτε ακολουθία στη δεξιά πλευρά.  Το sequence unpacking απαιτεί
να υπάρχουν τόσες μεταβλητές στην αριστερή πλευρά του συμβόλου
ισότητας όσα στοιχεία υπάρχουν στην ακολουθία.  Σημείωση ότι η
πολλαπλή ανάθεση είναι στην πραγματικότητα απλώς ένας συνδυασμός tuple
packing και sequence unpacking.


5.4. Σύνολα (Sets)
==================

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

Τα άγκιστρα ή η συνάρτηση "set()" μπορούν να χρησιμοποιηθούν για τη
δημιουργία συνόλων.  Σημείωση: για να δημιουργήσετε ένα κενό σύνολο
πρέπει να χρησιμοποιήσετε το "set()", όχι το "{}" ∙ το τελευταίο
δημιουργεί ένα κενό λεξικό, μια δομή δεδομένων που θα συζητήσουμε στην
επόμενη ενότητα.

Ακολουθεί μια σύντομη επίδειξη:

   >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
   >>> print(basket)                      # show that duplicates have been removed
   {'orange', 'banana', 'pear', 'apple'}
   >>> 'orange' in basket                 # fast membership testing
   True
   >>> 'crabgrass' in basket
   False

   >>> # Demonstrate set operations on unique letters from two words
   >>>
   >>> a = set('abracadabra')
   >>> b = set('alacazam')
   >>> a                                  # unique letters in a
   {'a', 'r', 'b', 'c', 'd'}
   >>> a - b                              # letters in a but not in b
   {'r', 'd', 'b'}
   >>> a | b                              # letters in a or b or both
   {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
   >>> a & b                              # letters in both a and b
   {'a', 'c'}
   >>> a ^ b                              # letters in a or b but not both
   {'r', 'd', 'b', 'm', 'z', 'l'}

Ομοίως με list comprehensions, υποστηρίζονται επίσης τα comprehensions
των συνόλων:

   >>> a = {x for x in 'abracadabra' if x not in 'abc'}
   >>> a
   {'r', 'd'}


5.5. Λεξικά (Dictionaries)
==========================

Another useful data type built into Python is the *dictionary* (see
Τύποι αντιστοίχισης --- dict). Dictionaries are sometimes found in
other languages as "associative memories" or "associative arrays".
Unlike sequences, which are indexed by a range of numbers,
dictionaries are indexed by *keys*, which can be any immutable type;
strings and numbers can always be keys.  Tuples can be used as keys if
they contain only strings, numbers, or tuples; if a tuple contains any
mutable object either directly or indirectly, it cannot be used as a
key. You can't use lists as keys, since lists can be modified in place
using index assignments, slice assignments, or methods like "append()"
and "extend()".

Είναι καλύτερο να σκεφτείτε ένα λεξικό ως ένα σύνολο ζευγών *key:
value*, με την προϋπόθεση ότι τα κλειδιά είναι μοναδικά (σε ένα
λεξικό). Ένα ζεύγος αγκύλων δημιουργεί ένα κενό λεξικό: "{}". Η
τοποθέτηση μιας λίστας ζευγών key:value στο λεξικό, αυτός είναι επίσης
ο τρόπος με τον οποίο γράφονται τα λεξικά στην έξοδο.

The main operations on a dictionary are storing a value with some key
and extracting the value given the key.  It is also possible to delete
a key:value pair with "del". If you store using a key that is already
in use, the old value associated with that key is forgotten.

Extracting a value for a non-existent key by subscripting ("d[key]")
raises a "KeyError". To avoid getting this error when trying to access
a possibly non-existent key, use the "get()" method instead, which
returns "None" (or a specified default value) if the key is not in the
dictionary.

Η εκτέλεση του "list(d)" σε ένα λεξικό επιστρέφει μια λίστα με όλα τα
κλειδιά που χρησιμοποιούνται στο λεξικό, με σειρά εισαγωγής (αν θέλετε
να ταξινομηθεί, απλώς χρησιμοποιήστε το "sorted(d)"). Για να ελέγξετε
εάν υπάρχει ένα μεμονωμένο κλειδί στο λεξικό, χρησιμοποιήστε τη
λέξη-κλειδί "in".

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

   >>> tel = {'jack': 4098, 'sape': 4139}
   >>> tel['guido'] = 4127
   >>> tel
   {'jack': 4098, 'sape': 4139, 'guido': 4127}
   >>> tel['jack']
   4098
   >>> tel['irv']
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   KeyError: 'irv'
   >>> print(tel.get('irv'))
   None
   >>> del tel['sape']
   >>> tel['irv'] = 4127
   >>> tel
   {'jack': 4098, 'guido': 4127, 'irv': 4127}
   >>> list(tel)
   ['jack', 'guido', 'irv']
   >>> sorted(tel)
   ['guido', 'irv', 'jack']
   >>> 'guido' in tel
   True
   >>> 'jack' not in tel
   False

Ο constructor "dict()" δημιουργεί λεξικά απευθείας από ακολουθίες
ζευγών key-value:

   >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
   {'sape': 4139, 'guido': 4127, 'jack': 4098}

Επιπλέον, τα comprehensions λεξικών μπορούν να χρησιμοποιηθούν για τη
δημιουργία λεξικών από αυθαίρετες εκφράσεις κλειδιού και τιμών:

   >>> {x: x**2 for x in (2, 4, 6)}
   {2: 4, 4: 16, 6: 36}

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

   >>> dict(sape=4139, guido=4127, jack=4098)
   {'sape': 4139, 'guido': 4127, 'jack': 4098}


5.6. Τεχνικές Looping
=====================

Κατά το looping μέσω λεξικών, το κλειδί και η αντίστοιχη τιμή μπορούν
να ανακτηθούν ταυτόχρονα χρησιμοποιώντας τη μέθοδο "items()".

   >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
   >>> for k, v in knights.items():
   ...     print(k, v)
   ...
   gallahad the pure
   robin the brave

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

   >>> for i, v in enumerate(['tic', 'tac', 'toe']):
   ...     print(i, v)
   ...
   0 tic
   1 tac
   2 toe

Για να κάνετε loop σε δύο ή περισσότερες ακολουθίες ταυτόχρονα, οι
καταχωρίσεις μπορούν να αντιστοιχιστούν με τη συνάρτηση "zip()".

   >>> questions = ['name', 'quest', 'favorite color']
   >>> answers = ['lancelot', 'the holy grail', 'blue']
   >>> for q, a in zip(questions, answers):
   ...     print('What is your {0}?  It is {1}.'.format(q, a))
   ...
   What is your name?  It is lancelot.
   What is your quest?  It is the holy grail.
   What is your favorite color?  It is blue.

Για να κάνετε loop σε μια ακολουθία αντίστροφα, καθορίστε πρώτα την
ακολουθία προς τα εμπρός και μετά καλέστε τη συνάρτηση "reversed()".

   >>> for i in reversed(range(1, 10, 2)):
   ...     print(i)
   ...
   9
   7
   5
   3
   1

Για να κάνετε loop σε μια ακολουθία με ταξινομημένη σειρά,
χρησιμοποιήστε τη συνάρτηση "sorted()", η οποία επιστρέφει μια νέα
ταξινομημένη λίστα αφήνοντας την πηγή αναλλοίωτη.

   >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
   >>> for i in sorted(basket):
   ...     print(i)
   ...
   apple
   apple
   banana
   orange
   orange
   pear

Η χρήση του "set()" σε μια ακολουθία εξαλείφει τα διπλά στοιχεία. Η
χρήση του "sorted()" σε συνδυασμό με το "set()" σε μια ακολουθία είναι
ένας ιδιωματικός τρόπος για να κάνετε loop πάνω από μοναδικά στοιχεία
της ακολουθίας σε ταξινομημένη σειρά.

   >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
   >>> for f in sorted(set(basket)):
   ...     print(f)
   ...
   apple
   banana
   orange
   pear

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

   >>> import math
   >>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
   >>> filtered_data = []
   >>> for value in raw_data:
   ...     if not math.isnan(value):
   ...         filtered_data.append(value)
   ...
   >>> filtered_data
   [56.2, 51.7, 55.3, 52.5, 47.8]


5.7. Περισσότερα για τις συνθήκες
=================================

Οι συνθήκες που χρησιμοποιούνται στις δηλώσεις "while" και "if"
μπορούν να περιέχουν οποιουσδήποτε τελεστές, όχι μόνο συγκρίσεις.

Οι τελεστές σύγκρισης "in" και "not in" είναι δοκιμές ιδιότητας μέλους
που καθορίζουν εάν μια τιμή βρίσκεται (ή όχι σε) ένα container.  Οι
τελεστές "is" και "is not" συγκρίνουν εάν δύο αντικείμενα είναι
πραγματικά το ίδιο αντικείμενο.  Όλοι οι τελεστές σύγκρισης έχουν την
ίδια προτεραιότητα, η οποία είναι χαμηλότερη από αυτή όλων των
αριθμητικών τελεστών.

Οι συγκρίσεις μπορούν να είναι αλυσιδωτές.  Για παράδειγμα, "a < b ==
c" ελέγχει εάν το "a" είναι μικρότερο από "b" και επιπλέον το "b"
ισούται με "c".

Οι συγκρίσεις μπορούν να συνδυαστούν χρησιμοποιώντας τους λογικούς
τελεστές "and" και "or", και το αποτέλεσμα μιας σύγκρισης (ή
οποιασδήποτε άλλης λογικής έκφρασης) μπορεί να ακυρωθεί με "not".
Αυτοί έχουν χαμηλότερες προτεραιότητες μεταξύ των τελεστών σύγκρισης,
το "not" έχει την υψηλότερη προτεραιότητα και το "or" τη χαμηλότερη,
έτσι ώστε το "A and not B or C" ισοδυναμεί με "(A and (not B)) or C".
Όπως πάντα, οι παρενθέσεις μπορούν να χρησιμοποιηθούν για να εκφράσουν
την επιθυμητή σύνθεση.

Οι λογικοί τελεστές "and" και "or" είναι οι λεγόμενοι τελεστές *short-
circuit*: τα ορίσματα τους αξιολογούνται από αριστερά προς τα δεξιά
και η αξιολόγηση σταματά μόλις καθοριστεί το αποτέλεσμα.  Για
παράδειγμα, εάν το "A" and "C" είναι αληθές, αλλά το "B" είναι ψευδές,
το "A and B and C" δεν αξιολογεί την έκφραση "C".  Όταν
χρησιμοποιείται ως γενική τιμή και όχι ως λογική, η τιμή επιστροφής
ενός short-circuit τελεστή είναι το τελευταίο αξιολογημένο όρισμα.

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

   >>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
   >>> non_null = string1 or string2 or string3
   >>> non_null
   'Trondheim'

Σημειώστε ότι στην Python, σε αντίθεση με την C, η ανάθεση εντός των
εκφράσεων πρέπει να γίνεται ρητά με τον τελεστή walrus operator ":=".
Αυτό αποφεύγει μια κοινή κατηγορία προβλημάτων που συναντώνται στα
προγράμματα C: πληκτρολογώντας "=" σε μια έκφραση όταν προοριζόταν το
"==".


5.8. Σύγκριση ακολουθιών και άλλων τύπων
========================================

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

   (1, 2, 3)              < (1, 2, 4)
   [1, 2, 3]              < [1, 2, 4]
   'ABC' < 'C' < 'Pascal' < 'Python'
   (1, 2, 3, 4)           < (1, 2, 4)
   (1, 2)                 < (1, 2, -1)
   (1, 2, 3)             == (1.0, 2.0, 3.0)
   (1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

Λάβετε υπόψη ότι η σύγκριση αντικειμένων διαφορετικών τύπων με "<" ή
">" είναι νόμιμη υπό τον όρο ότι τα αντικείμενα διαθέτουν κατάλληλες
μεθόδους σύγκρισης.  Για παράδειγμα, οι μικτές αριθμητικοί τύποι
συγκρίνονται σύμφωνα με την αριθμητική τους τιμή, οπότε το 0 ισούται
με 0.0, κλπ.  Διαφορετικά, αντί να παρέχει μια αυθαίρετη παραγγελία, ο
διερμηνέας θα κάνει raise μια εξαίρεση "TypeError".

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

[1] Άλλες γλώσσες ενδέχεται να επιστρέψουν το μεταλλαγμένο
    αντικείμενο, το οποίο επιτρέπει την αλυσιδωτή εκτέλεση μεθόδων,
    όπως "d->insert("a")->remove("b")->sort();".
