Η αποσυσκευασία αναφέρεται στην πράξη της εξαγωγής των στοιχείων μιας συλλογής, όπως μιας list, tuple ή dict, με χρήση επανάληψης.
Οι αποσυσκευασμένες τιμές μπορούν στη συνέχεια να ανατεθούν σε μεταβλητές μέσα στην ίδια εντολή.
Ένα πολύ συνηθισμένο παράδειγμα αυτής της συμπεριφοράς είναι το for item in list, όπου το item παίρνει διαδοχικά, στη διάρκεια της επανάληψης, την τιμή κάθε στοιχείου του list.
Η πολλαπλή ανάθεση είναι η δυνατότητα να αναθέτεις πολλές μεταβλητές σε αποσυσκευασμένες τιμές μέσα σε μία εντολή.
Αυτό επιτρέπει στον κώδικα να είναι πιο συνοπτικός και ευανάγνωστος, και γίνεται χωρίζοντας με κόμμα τις μεταβλητές που θα ανατεθούν, όπως first, second, third = (1,2,3) ή for index, item in enumerate(iterable).
Οι ειδικοί τελεστές * και ** χρησιμοποιούνται συχνά σε περιβάλλοντα αποσυσκευασίας.
Ο * μπορεί να χρησιμοποιηθεί για να συνδυάσεις πολλαπλά list/tuple σε ένα list/tuple, αποσυσκευάζοντας το καθένα σε ένα νέο κοινό list/tuple.
Ο ** μπορεί να χρησιμοποιηθεί για να συνδυάσεις πολλαπλά λεξικά σε ένα λεξικό, αποσυσκευάζοντας το καθένα σε ένα νέο κοινό dict.
Όταν ο τελεστής * χρησιμοποιείται χωρίς συλλογή, συσκευάζει έναν αριθμό τιμών σε ένα list.
Αυτό χρησιμοποιείται συχνά στην πολλαπλή ανάθεση για να ομαδοποιήσεις σε μία μόνο μεταβλητή όλα τα στοιχεία που "περισσεύουν" και δεν έχουν ξεχωριστή ανάθεση.
Στην Python είναι συνηθισμένο να αξιοποιείς αυτή τη συμπεριφορά αποσυσκευασίας/συσκευασίας και όταν χρησιμοποιείς ή ορίζεις συναρτήσεις που δέχονται οποιονδήποτε αριθμό ορισμάτων θέσης ή ορισμάτων με λέξη-κλειδί.
Θα δεις συχνά αυτές τις "ειδικές" παραμέτρους να ορίζονται ως def some_function(*args, **kwargs) και τα "ειδικά" ορίσματα να χρησιμοποιούνται ως some_function(*some_tuple, **some_dict).
Τα *<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.
Τα παρακάτω παραδείγματα χρησιμοποιούν lists, αλλά οι ίδιες έννοιες ισχύουν και για tuples.
Στην Python, είναι δυνατό να αποσυσκευάσεις τα στοιχεία ενός list/tuple/dict σε ξεχωριστές μεταβλητές.
Επειδή οι τιμές εμφανίζονται μέσα σε 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']
# A list literal can be used instead, but might not be as readable.
>>> [*combined_fruits_too] = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
Για περισσότερες λεπτομέρειες σχετικά με τη χρήση των * και **, δες τα PEP 3132 και PEP 448.
**
Η συσκευασία ενός λεξικού γίνεται με τον τελεστή **.
Αυτό θα συσκευάσει όλα τα ζεύγη κλειδιού-τιμής από ένα λεξικό σε ένα άλλο λεξικό, ή θα συνδυάσει δύο λεξικά μαζί.
>>> 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 χρησιμοποιείται για να συσκευάσει οποιονδήποτε αριθμό ορισμάτων θέσης (χωρίς λέξη-κλειδί) και
το **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.
Τα ορίσματα πρέπει να έχουν τη δομή που φαίνεται παρακάτω:
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)]