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

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

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

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

list.append(value, /)

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

list.extend(iterable, /)

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

list.insert(index, value, /)

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

list.remove(value, /)

  Remove the first item from the list whose value is equal to value. It raises a `ValueError` if there is no such item.

list.pop(index=-1, /)

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

list.clear()

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

list.index(value[, start[, stop]])

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

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

list.count(value, /)

  Return the number of times value appears in the list.

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)

Οι μέθοδοι λίστας καθιστούν πιο εύκολη τη χρήση μιας λίστα ως στοίβας (stack), όπου το τελευταίο στοιχείο που προστέθηκε είναι το πρώτο στοιχείο που ανακτήθηκε («last-in, first-out»). Για να προσθέσετε ένα στοιχείο στην κορυφή της στοίβας, χρησιμοποιήστε την `append()`. Για να ανακτήσετε ένα στοιχείο από την κορυφή της στοίβας, χρησιμοποιήστε την `pop()` χωρίς κάποιο σαφές index. Για παράδειγμα:

    >>> 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.1.5. Unpacking in Lists and List Comprehensions

The section on Unpacking Λίστες Ορισμάτων describes the use of `*` to «unpack» the elements of an iterable object, providing each one separately as an argument to a function. Unpacking can also be used in other contexts, for example, when creating lists. When specifying elements of a list, prefixing an expression by a `*` will unpack the result of that expression, adding each of its elements to the list we’re creating:

    >>> x = [1, 2, 3]
    >>> [0, *x, 4, 5, 6]
    [0, 1, 2, 3, 4, 5, 6]

This only works if the expression following the `*` evaluates to an iterable object; trying to unpack a non-iterable object will raise an exception:

    >>> x = 1
    >>> [0, *x, 2, 3, 4]
    Traceback (most recent call last):
      File "<python-input-1>", line 1, in <module>
        [0, *x, 2, 3, 4]
    TypeError: Value after * must be an iterable, not int

Unpacking can also be used in list comprehensions, as a way to build a new list representing the concatenation of an arbitrary number of iterables:

    >>> x = [[1, 2, 3], [4, 5, 6], [], [7], [8, 9]]
    >>> [*element for element in x]
    [1, 2, 3, 4, 5, 6, 7, 8, 9]

Note that the effect is that each element from `x` is unpacked. This works for arbitrary iterable objects, not just lists:

    >>> x = [[1, 2, 3], 'cat', {'spam': 'eggs'}]
    >>> [*element for element in x]
    [1, 2, 3, 'c', 'a', 't', 'spam']

But if the objects in `x` are not iterable, this expression would again raise an exception.

## 5.2. Η δήλωση `del`

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

    >>> 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])
    >>> # they support unpacking just like lists:
    >>> x = [1, 2, 3]
    >>> 0, *x, 4
    (0, 1, 2, 3, 4)

Όπως βλέπετε, οι πλειάδες (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()`, όχι το `{}` ∙ το τελευταίο δημιουργεί ένα κενό λεξικό, μια δομή δεδομένων που θα συζητήσουμε στην επόμενη ενότητα.

Because sets are unordered, iterating over them or printing them can produce the elements in a different order than you expect.

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

    >>> 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'}

Similarly to list comprehensions, set comprehensions are also supported, including comprehensions with unpacking:

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

    >>> fruits = [{'apple', 'avocado', 'apricot'}, {'banana', 'blueberry'}]
    >>> {*fruit for fruit in fruits}
    {'blueberry', 'banana', 'avocado', 'apple', 'apricot'}

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

Ένα άλλος χρήσιμος τύπος δεδομένων που είναι ενσωματωμένος στην Python είναι το dictionary (βλ. Mapping types — dict, frozendict). Τα λεξικά βρίσκονται μερικές φορές σε άλλες γλώσσες ως «συσχετιστικές μνήμες» ή «συσχετιστικοί πίνακες». Σε αντίθεση με τις ακολουθίες, οι οποίες είναι με indexes με ένα εύρος αριθμών, τα λεξικά γίνονται index με keys, που μπορεί να είναι οποιοσδήποτε αμετάβλητος τύπος∙ οι συμβολοσειρές και οι αριθμοί μπορούν πάντα να είναι κλειδιά. Οι πλειάδες (tuples) μπορούν να χρησιμοποιηθούν ως κλειδιά εάν περιέχουν μόνο συμβολοσειρές, αριθμούς ή πλειάδες (tuples)∙ εάν μια πλειάδα περιέχει οποιοδήποτε μεταβλητό αντικείμενο είτε άμεσα είτε έμμεσα, δεν μπορεί να χρησιμοποιηθεί ως κλειδί. Δεν μπορείτε να χρησιμοποιήσετε λίστες ως κλειδιά, καθώς οι λίστες μπορούν να τροποποιηθούν επιτόπου χρησιμοποιώντας αναθέσεις index, αναθέσεις slice, ή μεθόδους όπως `append()` και `extend()`.

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

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

Η εξαγωγή μιας τιμής για ένα μη υπάρχον κλειδί με δεικτοδότηση (`d[key]`) προκαλεί ένα `KeyError`. Για να αποφύγετε αυτό το σφάλμα όταν προσπαθείτε να αποκτήσετε πρόσβαση σε ένα πιθανώς μη υπάρχον κλειδί, χρησιμοποιήστε αντίθετα τη μέθοδο `get()`, η οποία επιστρέφει `None` (ή μια καθορισμένη προεπιλεγμένη τιμή) εάν το κλειδί δεν βρίσκεται στο λεξικό.

Η εκτέλεση του `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}

And dictionary unpacking (via `**`) can be used to merge multiple dictionaries:

    >>> odds = {i: i**2 for i in (1, 3, 5)}
    >>> evens = {i: i**2 for i in (2, 4, 6)}
    >>> {**odds, **evens}
    {1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36}

    >>> all_values = [odds, evens, {0: 0}]
    >>> {**i for i in all_values}
    {1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36, 0: 0}

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

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