Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

4. Πλειάδες και λεξικά

ΤΜΧΠΠΑ-ΠΘ

Στην συνέχεια παρουσιάζονται δύο άλλες καθιερωμένες δομές στην Python, οι πλειάδες (tuples) και τα λεξικά (dictionaries).

Πλειάδες (tuples)

Οι πλειάδες είναι μια δομή δεδομένων παρόμοια με τις λίστες. Όπως και οι λίστες αποτελούν μια συλλογή αντικειμένων (objects). Όμως διαφέρουν σε ένα σημαντικό χαρακτηριστικό από τις λίστες, είναι αμετάβλητες (immutable), δηλαδή δεν μπορούμε να μεταβάλλουμε το περιεχόμενό τους εφόσον τις δημιουργήσουμε. Κατά συνέπεια ενώ για την προσπέλαση των στοιχείων της χρησιμοποιούνται ίδιες τεχνικές και μέθοδοι (indexing, slicing κτλ) δεν υποστηρίζουν ωστόσο τις αντίστοιχες μεθόδους αφαίρεσης, τροποποίησης και προσθήκης στοιχείων. Υπό την έννοια αυτή οι πλειάδες είναι μια ασφαλή δομή δεδομένων μέσω της οποίας εξασφαλίζεται ότι τα δεδομένα δεν θα τροποποιηθούν από λάθος ή ακόμα και από επιλογή. Συνήθως χρησιμοποιούνται κατά την επιστροφή πολλαπλών τιμών από μια συνάρτηση αλλά και όταν θέλουμε να “πακετάρουμε” δεδομένα (tuple packing). Οι πλειάδες ορίζονται με παρόμοιο τρόπο σαν τις λίστες αλλά αντί για αγκύλες χρησιμοποιούνται παρενθέσεις (αν και δεν είναι απαραίτητες). Για παράδειγμα:

information = ('UTH','https://www.uth.gr/')

Το ίδιο μπορεί να γραφτεί και χωρίς παρενθέσεις

information = 'UTH','https://www.uth.gr/'

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

year = (2022)
print(type(year))
<class 'int'>

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

year = (2022,)
print(type(year))
<class 'tuple'>

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

empty_tuple = ()

Επιπλέον μέσω της συνάρτησης tuple η οποία δέχεται ως όρισμα μία συμβολοσειρά ή λίστα μπορούμε να δημιουργήσουμε νέες πλειάδες. Προσέξτε το αποτέλεσμα όταν ορίζουμε σαν παράμετρο συμβολοσειρά στην συνάρτηση tuple.

regions= tuple("Κρήτη")
print(regions)
('Κ', 'ρ', 'ή', 'τ', 'η')
regions= tuple(["Κρήτη", "Ήπειρος", "Θράκη"])
print(regions)
('Κρήτη', 'Ήπειρος', 'Θράκη')

Με κενό όρισμα επιστρέφεται μία άδεια πλειάδα:

regions= tuple()

Όπως προαναφέρθηκε οι πλειάδες είνα αμετάβλητες δομές δεδομένων και έτσι δεν μπορούμε να χρησιμοποιήσουμε μεθόδους όπως append, del, sort κ.α. Μπορούμε, όπως και στις λίστες, να αναφερθούμε στα στοιχεία της πλειάδας με τους τελεστές [] και με το ευρετήριο (index) ή να εξάγουμε τμήμα από τα στοιχεία (slice) με τον τελεστή :. Λαμβάνουμε το πλήθος των στοιχείων της πλειάδας (lengh) με την συνάρτηση len.

Οι πλειάδες χρησιμοποιούνται για να πακετάρουμε μια συλλογή δεδομένων σε ένα αντικείμενο (tuple packing) π.χ.

fruit1 = "Μήλος"
fruit2 = "Πορτοκάλι"
fruit1 = "Μανταρίνι"
fruits = fruit1, fruit1, fruit2 # tuple packing
print(fruits)
('Μανταρίνι', 'Μανταρίνι', 'Πορτοκάλι')

Βέβαια υπάρχει και η αντίστροφη διαδικασία, το “ξε-πακετάρισμα” δεδομένων (tuple unpacking). Όταν δηλαδή αποδίδουμε τα στοιχεία της πλειάδας σε ξεχωριστές μεταβλητές.

cities = "Αθήνα", "Βόλος", "Πάτρα"
capital, city1, city2 = cities # tuple unpacking
print(capital,  city1, city2)
Αθήνα Βόλος Πάτρα

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

cities = ("Αθήνα", "Βόλος", "Πάτρα")
cities.pop(1)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[11], line 2
      1 cities = ("Αθήνα", "Βόλος", "Πάτρα")
----> 2 cities.pop(1)

AttributeError: 'tuple' object has no attribute 'pop'
cities.append("Ρόδος")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[12], line 1
----> 1 cities.append("Ρόδος")

AttributeError: 'tuple' object has no attribute 'append'

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

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

mytuple = (1, 2, 3)
mytuple[0] = 999
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[13], line 2
      1 mytuple = (1, 2, 3)
----> 2 mytuple[0] = 999

TypeError: 'tuple' object does not support item assignment
island='Λέσβος'
island2='Χίος'
island, island2 = island2, island

print(island)
print(island2)
Χίος
Λέσβος
cities = ("Αθήνα", ["Βόλος", "Πάτρα"])
cities[1][0] = "Καβάλα"
print(cities)
('Αθήνα', ['Καβάλα', 'Πάτρα'])

Μια εξήγηση σε αυτήν την αντιφατική συμπεριφορά δίνεται στο παρακάτω νήμα: https://stackoverflow.com/questions/9755990/why-can-tuples-contain-mutable-items

Λεξικά (Dictionaries)

Τα λεξικά στην Python αποτελούν συλλογές αντικειμένων, όπως οι λίστες και οι πλειάδες. Ένα σημαντικό χαρακτηριστικό των λεξικών είναι ότι αυτά αποθηκεύουν δεδομένα κατά ζεύγη, με την μορφή κλειδί-τιμή (key-value pairs). Κάθε κλειδί σε ένα λεξικό συνοδεύεται από μία τιμή. Κάθε κλειδί αποτελεί ουσιαστικά ένα μοναδικό αναγνωριστικό για την συνοδευτική τιμή και γι’αυτό τον λόγο δεν μπορεί να υπάρξει δεύτερο ίδιο κλειδί. Ακόμα, τα κλειδιά πρέπει να ορίζονται από αμετάβλητους τύπους δεδομένων δηλαδή είτε από μιά συμβολοσειρά είτε από έναν ακέραιο ή δεκαδικό. Δεν μπορεί όμως μια λίστα να είναι κλειδί. Μια πλειάδα μπορεί να είναι κλειδί αλλά με την προϋπόθεση ότι και αυτή δεν θα αποτελείται από μεταβλητούς τύπους δεδομένων. Τα λεξικά περικλείονται σε {}, τα ζεύγη ορίζονται υπό την μορφή κλειδί:τιμή και χωρίζονται μεταξύ τους με κόμμα (,) δηλαδή:

d = {key1 : value1, key2 : value2 }

Για παράδειγμα:

phones ={"Χρήστος": "69936565", "Κώστας":"246541353", "Βαγγέλης":"546546536"}
print(phones)
{'Χρήστος': '69936565', 'Κώστας': '246541353', 'Βαγγέλης': '546546536'}

Τα κλειδιά (keys) σε αυτήν την περίπτωση είναι τα ονόματα και οι αριθμοί τηλεφώνων (ως συμβολοσειρές ορισμένες) οι τιμές (values).

Εναλλακτικά μπορούμε να δημιουργήσουμε ένα άδειο λεξικό και να προσθέσουμε στην συνέχεια ζεύγη.

phones ={}
phones["Χρήστος"] ="69936565"
phones["Κώστας"]  ="246541353"
phones["Βαγγέλης"]="546546536"

Ένας άλλος τρόπος δημιουργίας λεξικών είναι με την συνάρτηση dict.

phones =dict(Χρήστος="69936565",Κώστας="246541353", Βαγγέλης="546546536")

Με αυτόν τον τρόπο ορίζουμε τα κλειδιά μέσω μεταβλητών και γι’ αυτό τον λόγο εφαρμόζονται οι περιορισμοί που αφορούν την ονοματολογία των μεταβλητών.

Στα λεξικά τα ζεύγη αυτά δεν ταξινομούνται με κάποια συγκεκριμένη σειρά αλλά με έναν μηχανισμό της Python που λέγεται hashing και αποσκοπεί στην γρήγορη ανάκτηση τους. Η ταξινόμηση αυτή αλλάζει κάθε φορά κατά τυχαίο τρόπο όταν τροποποιούμε ένα λεξικό. Για τον λόγο αυτό δεν υπάρχει η έννοια της θέσης ή του δείκτη (index) όπως στις λίστες και στις πλειάδες. Η ανάκτηση μιας τιμής από ένα ζεύγος γίνεται με βάση τo κλειδί π.χ.* d[‘a_key’]*. Γίνεται δηλαδή με παρόμοιο τρόπο όπως στις λίστες και τις πλειάδες αλλά αντι για το ευρετήριο θέσης ορίζουμε το κλειδί.

print(phones["Χρήστος"])
print(phones["Βαγγέλης"])
69936565
546546536

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

print(phones["Βαγγέλης"])
phones["Βαγγέλης"]="666666666"
print(phones["Βαγγέλης"])
546546536
666666666

Για την διαγραφή ενός ζεύγους από λεξικό χρησιμοποιείται η συνάρτηση del δηλ. del(d[‘key’]). Δείτε το επόμενο παράδειγμα:

del(phones["Βαγγέλης"])
print(phones)
{'Χρήστος': '69936565', 'Κώστας': '246541353'}

Με την συνάρτηση len επιστρέφεται το μέγεθος του λεξικού δηλαδή το πλήθος των ζευγαριών κλειδιών/τιμών που περιέχει:

print(len(phones))
2

Μέσω του τελεστή *in * διαπιστώνεται αν υπάρχει ένα κλειδί σε ένα λεξικό:

print("Χρήστος" in  phones)
True

Με την μέθοδο keys ενός λεξικού επιστρέφονται τα κλειδιά του. Αντίστοιχα με την μέθοδο values επιστρέφονται οι τιμές του ενώ με την μέθοδο items επιστρέφονται τα ζεύγη κλειδιών/τιμών. Τα επιστρεφόμενα objects είναι αντιστοιχα dict_keys, dict_values, dict_items. Ο παρακάτω κώδικας περιγράφει τις παραπάνω λειτουργίες:

d = {'a': 10, 'b': 20, 'c': 30}
print(d.keys())
print(d.values())
print(d.items())
dict_keys(['a', 'b', 'c'])
dict_values([10, 20, 30])
dict_items([('a', 10), ('b', 20), ('c', 30)])

Αν θέλουμε να ταξινομήσουμε το λεξικό με βάση τα κλειδιά τότε το κάνουμε με την συνάρτηση sorted:

thisdict = {
  "year": 1964,
  "brand": "Ford",
  "model": "Mustang"
}

thisdict_sorted = {k: v for k, v in sorted(thisdict.items(), key=lambda item: item[0])}
print(thisdict_sorted)
{'brand': 'Ford', 'model': 'Mustang', 'year': 1964}

Πολύ συνηθισμένη περίπτωση είναι τα λεξικά να περιλαμβάνουν σαν τιμές άλλα λεξικά.

contacts = {"Χρήστος": {"Σπίτι": "457456456", "Εργασία": "48856"}, 
            "Γιάννης": {"Σπίτι": "8753778", "Εργασία": "45654656"}, 
            "Κώστας": {"Κινητό": "45475354"}}

Σε αυτή την περίπτωση ορίζοντας διαδοχικά τα κλειδιά παίρνουμε τις τιμές που επιθυμούμε:

print(contacts["Χρήστος"])
print(contacts["Χρήστος"]["Εργασία"])
{'Σπίτι': '457456456', 'Εργασία': '48856'}
48856

Μπορούμε να διατρέξουμε τα ζεύγη ενός λεξικού αλλά η σειρά που θα γίνεται η ανάκτηση μπορεί να μην είναι με την σειρά που ορίζονται και όχι πάντα η ίδια.

for phone in phones:
    print(phones[phone])
69936565
246541353

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

names1 = {'name':"Κώστας"}
names2=names1 
names2['name']="Γιάννης"
print(names1)
{'name': 'Γιάννης'}

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

names1 = {'name':"Κώστας"}
names2=names1.copy()
names2['name']="Γιάννης"
print(names1)
{'name': 'Κώστας'}