Μηχανοδηγός

Μηχανοδηγός

Άσκηση εκμάθησης

Εισαγωγή

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

Οι ειδικοί τελεστές * και ** χρησιμοποιούνται συχνά σε περιβάλλοντα αποσυσκευασίας και με πολλαπλή ανάθεση.

Caution

Τα *<variable_name> και **<variable_name> δεν πρέπει να συγχέονται με τα * και **. Ενώ τα * και ** χρησιμοποιούνται για πολλαπλασιασμό και ύψωση σε δύναμη αντίστοιχα, τα *<variable_name> και **<variable_name> χρησιμοποιούνται ως τελεστές συσκευασίας και αποσυσκευασίας.

Πολλαπλή ανάθεση

Στην πολλαπλή ανάθεση, ο αριθμός των μεταβλητών στην αριστερή πλευρά του τελεστή ανάθεσης (=) πρέπει να ταιριάζει με τον αριθμό των τιμών στη δεξιά πλευρά. Για να χωρίσεις τις τιμές, χρησιμοποίησε κόμμα ,:

>>> a, b = 1, 2
>>> a
1

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

>>> x, y, z = 1, 2

ValueError: too many values to unpack (expected 3, got 2)

Η πολλαπλή ανάθεση δεν περιορίζεται σε έναν μόνο τύπο δεδομένων:

>>> x, y, z = 1, "Hello", True
>>> x
1

>>> y
'Hello'

>>> z
True

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

>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]

Επειδή τα tuples είναι αμετάβλητα, δεν μπορείς να ανταλλάξεις στοιχεία σε ένα tuple.

Αποσυσκευασία

Note

Τα παρακάτω παραδείγματα χρησιμοποιούν lists, αλλά οι ίδιες έννοιες ισχύουν και για tuples.

Στην Python, είναι δυνατό να αποσυσκευάσεις τα στοιχεία μιας list/tuple/dictionary σε ξεχωριστές μεταβλητές. Επειδή οι τιμές εμφανίζονται μέσα σε lists/tuples με συγκεκριμένη σειρά, αποσυσκευάζονται σε μεταβλητές με την ίδια σειρά:

>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"

Αν υπάρχουν τιμές που δεν χρειάζεσαι, τότε μπορείς να χρησιμοποιήσεις το _ για να τις σημάνεις:

>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"

Βαθιά αποσυσκευασία

Η αποσυσκευασία και η ανάθεση τιμών από μια list/tuple που περικλείεται μέσα σε μια list ή tuple (γνωστές και ως εμφωλευμένες λίστες/πλειάδες) λειτουργεί με τον ίδιο τρόπο όπως μια ρηχή αποσυσκευασία, αλλά συχνά χρειάζεται προσδιοριστές για να ξεκαθαρίσει το πλαίσιο ή η θέση:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"

>>> d
"potato"

Μπορείς επίσης να αποσυσκευάσεις βαθιά μόνο ένα τμήμα μιας εμφωλευμένης list/tuple:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]

>>> c
"carrot"

Αν η αποσυσκευασία έχει μεταβλητές με λανθασμένη τοποθέτηση ή/και λανθασμένο αριθμό τιμών, θα πάρεις ένα ValueError:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [d]] = fruits_vegetables

ValueError: too many values to unpack (expected 1)

Αποσυσκευασία μιας λίστας/πλειάδας με *

Όταν αποσυσκευάζεις μια list/tuple, μπορείς να χρησιμοποιήσεις τον τελεστή * για να συλλάβεις τις "υπόλοιπες" τιμές. Αυτό είναι πιο ξεκάθαρο από το τεμαχισμό της list/tuple (που σε ορισμένες περιπτώσεις είναι λιγότερο ευανάγνωστο). Για παράδειγμα, το πρώτο στοιχείο μπορεί να εξαχθεί και στη συνέχεια οι υπόλοιπες τιμές μπορούν να τοποθετηθούν σε μια νέα list χωρίς το πρώτο στοιχείο:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *last = fruits
>>> x
"apple"

>>> last
["banana", "cherry", "orange", "kiwi", "melon", "mango"]

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

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *middle, y, z = fruits
>>> y
"melon"

>>> middle
["banana", "cherry", "orange", "kiwi"]

Μπορούμε επίσης να χρησιμοποιήσουμε το * στη βαθιά αποσυσκευασία:

>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>>> [[a, *rest], b] = fruits_vegetables
>>> a
"apple"

>>> rest
["banana", "melon"]

Αποσυσκευασία ενός λεξικού

Η αποσυσκευασία ενός λεξικού είναι λίγο διαφορετική από την αποσυσκευασία μιας list/tuple. Η επανάληψη πάνω σε λεξικά προεπιλέγει τα κλειδιά. Έτσι, όταν αποσυσκευάζεις ένα dict, μπορείς να αποσυσκευάσεις μόνο τα κλειδιά και όχι τις τιμές:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"

Αν θέλεις να αποσυσκευάσεις τις τιμές, τότε μπορείς να χρησιμοποιήσεις τη μέθοδο <dict>.values():

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6

Αν χρειάζονται και τα κλειδιά και οι τιμές, χρησιμοποίησε τη μέθοδο <dict>.items(). Η <dict>.items() παράγει μια επαναλήψιμη προβολή που περιέχει ζεύγη κλειδιού-τιμής. Αυτά μπορούν να αποσυσκευαστούν σε ένα tuple:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)

Συσκευασία

Η συσκευασία είναι η δυνατότητα να ομαδοποιήσεις πολλαπλές τιμές σε μία list που ανατίθεται σε μια μεταβλητή. Αυτό είναι χρήσιμο όταν θέλεις να αποσυσκευάσεις τιμές, να κάνεις αλλαγές και στη συνέχεια να συσκευάσεις τα αποτελέσματα ξανά σε μια μεταβλητή. Κάνει επίσης δυνατή την εκτέλεση συγχωνεύσεων σε 2 ή περισσότερες lists/tuples/dicts.

Συσκευασία μιας λίστας/πλειάδας με *

Η συσκευασία μιας list/tuple μπορεί να γίνει χρησιμοποιώντας τον τελεστή *. Αυτό θα συσκευάσει όλες τις τιμές σε μια list/tuple.

>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]

# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>>> combined_fruits = *fruits, *more_fruits

# If there is no * on to the left of the "=" the result is a tuple
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")

# If the * operator is used on the left side of "=" the result is a list.
# Note the trailing comma.
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

Για περισσότερες πληροφορίες σχετικά με τη χρήση του * στην αριστερή πλευρά, δες το PEP 3132.

Συσκευασία ενός λεξικού με **

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

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}

# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

Χρήση των * και ** με συναρτήσεις

Συσκευασία με παραμέτρους συνάρτησης

Όταν δημιουργείς μια συνάρτηση που δέχεται αυθαίρετο αριθμό ορισμάτων, μπορείς να χρησιμοποιήσεις τα *args ή **kwargs στον ορισμό της συνάρτησης. Το *args χρησιμοποιείται για να συσκευάσει έναν αυθαίρετο αριθμό ορισμάτων θέσης (μη λέξεων-κλειδιών) ως tuple και το **kwargs χρησιμοποιείται για να συσκευάσει έναν αυθαίρετο αριθμό ορισμάτων με λέξεις-κλειδιά ως λεξικό.

Χρήση του *args:

# This function is defined to take any number of positional arguments

>>> def my_function(*args):
...     print(args)

# Arguments given to the function are packed into a tuple

>>> my_function(1, 2, 3)
(1, 2, 3)

>>> my_function("Hello")
("Hello")

>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")

Χρήση του **kwargs:

# This function is defined to take any number of keyword arguments

>>> def my_function(**kwargs):
...   print(kwargs)

# Arguments given to the function are packed into a dictionary

>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}

Τα *args και **kwargs μπορούν επίσης να χρησιμοποιηθούν σε συνδυασμό μεταξύ τους:

>>> def my_function(*args, **kwargs):
...   print(sum(args))
...   for key, value in kwargs.items():
...       print(str(key) + " = " + str(value))

>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
a = 1
b = 2
c = 3

Μπορείς επίσης να γράψεις παραμέτρους πριν από το *args για να επιτρέψεις συγκεκριμένα ορίσματα θέσης. Τα μεμονωμένα ορίσματα με λέξεις-κλειδιά πρέπει τότε να εμφανίζονται πριν από το **kwargs.

Caution

Τα ορίσματα πρέπει να είναι δομημένα έτσι:

def my_function(<positional_args>, *args, <key-word_args>, **kwargs)

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

>>> def my_function(a, b, *args):
...   print(a)
...   print(b)
...   print(args)

>>> my_function(1, 2, 3, 4, 5)
1
2
(3, 4, 5)

Η εγγραφή των ορισμάτων σε λανθασμένη σειρά θα έχει ως αποτέλεσμα ένα σφάλμα:

>>>def my_function(*args, a, b):
... print(args)

>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
  File "c:\something.py", line 3, in <module>
    my_function(1, 2, 3, 4, 5)
TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'

Αποσυσκευασία σε κλήσεις συναρτήσεων

Μπορείς να χρησιμοποιήσεις το * για να αποσυσκευάσεις μια list/tuple ορισμάτων σε μια κλήση συνάρτησης. Αυτό είναι πολύ χρήσιμο για συναρτήσεις που δεν δέχονται ένα iterable:

>>> def my_function(a, b, c):
...   print(c)
...   print(b)
...   print(a)

numbers = [1, 2, 3]
>>> my_function(*numbers)
3
2
1

Η χρήση της αποσυσκευασίας με * μαζί με την ενσωματωμένη συνάρτηση zip() είναι άλλη μια συνηθισμένη περίπτωση χρήσης. Η συνάρτηση zip() δέχεται πολλαπλά επαναλήψιμα και επιστρέφει μια list από tuples με τις τιμές από κάθε iterable ομαδοποιημένες:

>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]

Οδηγίες

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

Note

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

1. Δημιούργησε μια λίστα με όλα τα βαγόνια

Ο φίλος σου καταγράφει το αναγνωριστικό (ID) κάθε βαγονιού, αλλά ποτέ δεν είναι σίγουρος πόσα βαγόνια θα πρέπει να επεξεργαστεί το σύστημα κάθε φορά. Θα ήταν πολύ πιο εύκολο για το υπόλοιπο πρόγραμμα logistics να έχει αυτά τα δεδομένα πακεταρισμένα σε μία ενιαία list.

Υλοποίησε μια συνάρτηση get_list_of_wagons() που δέχεται οποιονδήποτε αριθμό ID βαγονιών. Κάθε ID θα είναι ένας θετικός ακέραιος. Η συνάρτηση θα πρέπει στη συνέχεια να κάνει return τα δοσμένα ID ως μία ενιαία list.

>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
[1, 7, 12, 3, 14, 8, 5]

2. Διόρθωσε τη λίστα των βαγονιών

Σε αυτό το σημείο, αρχίζεις να αποκτάς μια αίσθηση για τα δεδομένα και για το πώς χρησιμοποιούνται στο πρόγραμμα logistics. Το σύστημα ID δίνει πάντα στη μηχανή ένα ID 1, ενώ στα υπόλοιπα βαγόνια του τρένου δίνει ένα τυχαία επιλεγμένο ID μεγαλύτερο του 1.

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

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

Ο Linus θα σου ήταν πραγματικά ευγνώμων αν διόρθωνες τα λάθη του και ενοποιούσες τα δεδομένα.

Υλοποίησε μια συνάρτηση fix_list_of_wagons() που δέχεται δύο lists που περιέχουν ID βαγονιών. Θα πρέπει να μετακινήσει τα δύο πρώτα στοιχεία της πρώτης list στο τέλος και να εισαγάγει τις τιμές από τη δεύτερη list πίσω (στα δεξιά) από το ID της μηχανής (1). Η συνάρτηση θα πρέπει στη συνέχεια να κάνει return μια list με τις τροποποιήσεις.

>>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15])
[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5]

3. Πρόσθεσε τις στάσεις που λείπουν

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

Υλοποίησε μια συνάρτηση add_missing_stops() που δέχεται μια dict δρομολόγησης ακολουθούμενη από έναν μεταβλητό αριθμό ορισμάτων με λέξεις-κλειδιά. Αυτά τα ορίσματα μπορεί να έχουν τη μορφή μιας dict που περιέχει μία ή περισσότερες στάσεις, ή οποιουδήποτε αριθμού ζευγών λέξεων-κλειδιών της μορφής stop_number=city. Η συνάρτησή σου θα πρέπει στη συνέχεια να επιστρέψει τη dict δρομολόγησης ενημερωμένη με ένα επιπλέον key που περιέχει μια list με όλες τις στάσεις που προστέθηκαν, με τη σειρά.

>>> add_missing_stops({"from": "New York", "to": "Miami"},
                      stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta",
                      stop_4="Jacksonville", stop_5="Orlando")

{"from": "New York", "to": "Miami", "stops": ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]}

4. Επέκτεινε τις πληροφορίες δρομολόγησης

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

Υλοποίησε μια συνάρτηση με το όνομα extend_route_information() που δέχεται δύο dicts. Η πρώτη dict περιέχει τις πόλεις αφετηρίας και προορισμού ανάμεσα στις οποίες εκτελείται η διαδρομή του τρένου.

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

Note

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

>>> extend_route_information({"from": "Berlin", "to": "Hamburg"}, {"length": "100", "speed": "50"})
{"from": "Berlin", "to": "Hamburg", "length": "100", "speed": "50"}

5. Διόρθωσε το αμαξοστάσιο

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

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

Υλοποίησε μια συνάρτηση με το όνομα fix_wagon_depot() που δέχεται μια list με τρία στοιχεία. Κάθε στοιχείο της list είναι μια υπολίστα (ή "γραμμή") που περιέχει τρία tuples. Κάθε tuple είναι ένα ζεύγος (<wagon ID>, <wagon color>).

Η συνάρτησή σου θα πρέπει να επιστρέψει μια list με τις τρεις "γραμμές" lists αναδιατεταγμένες, ώστε τα βαγόνια να ανταλλαγούν στις σωστές θέσεις τους.

>>> fix_wagon_depot([
                    [(2, "red"), (4, "red"), (8, "red")],
                    [(5, "blue"), (9, "blue"), (13,"blue")],
                    [(3, "orange"), (7, "orange"), (11, "orange")],
                    ])

[
[(2, "red"), (5, "blue"), (3, "orange")],
[(4, "red"), (9, "blue"), (7, "orange")],
[(8, "red"), (13,"blue"), (11, "orange")]
]
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
Python Exercism

Έτοιμος να ξεκινήσεις την άσκηση Μηχανοδηγός;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις Python με 17 έννοιες146 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.