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

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


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

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

list.append(x)

   Add an item to the end of the list.  Equivalent to "a[len(a):] =
   [x]".

list.extend(iterable)

   Extend the list by appending all the items from the iterable.
   Equivalent to "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])

   Remove the item at the given position in the list, and return it.
   If no index is specified, "a.pop()" removes and returns the last
   item in the list.  (The square brackets around the *i* in the
   method signature denote that the parameter is optional, not that
   you should type square brackets at that position.  You will see
   this notation frequently in the Python Library Reference.)

list.clear()

   Remove all items from the list.  Equivalent to "del a[:]".

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

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

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

list.count(x)

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

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

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

list.reverse()

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

list.copy()

   Return a shallow copy of the list.  Equivalent to "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 a 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.

Another thing you might notice is that not all data can be sorted or
compared.  For instance, "[None, 'hello', 10]" doesn't sort because
integers can't be compared to strings and *None* can't be compared to
other types.  Also, there are some types that don't have a defined
ordering relation.  For example, "3+4j < 5+7j" isn't a valid
comparison.


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]]

As we saw in the previous section, the nested listcomp is evaluated in
the context of the "for" that follows it, so this example is
equivalent to:

   >>> 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 στο λεξικό, αυτός είναι επίσης
ο τρόπος με τον οποίο γράφονται τα λεξικά στην έξοδο.

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

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

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

   >>> tel = {'jack': 4098, 'sape': 4139}
   >>> tel['guido'] = 4127
   >>> tel
   {'jack': 4098, 'sape': 4139, 'guido': 4127}
   >>> tel['jack']
   4098
   >>> 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
=====================

When looping through dictionaries, the key and corresponding value can
be retrieved at the same time using the "items()" method.

   >>> 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();".
