Tracce
/
Python
Python
/
Programma
/
Unpacking e assegnazione multipla
Un

Unpacking e assegnazione multipla in Python

3 esercizi

Informazioni su Unpacking e assegnazione multipla

Lo spacchettamento è l'atto di estrarre gli elementi di una collezione, come una list, una tuple o un dict, usando l'iterazione. I valori così spacchettati possono poi essere assegnati a delle variabili all'interno della stessa istruzione. Un esempio molto comune di questo comportamento è for item in list, dove item assume a turno il valore di ogni elemento della list durante l'iterazione.

L'assegnazione multipla è la capacità di assegnare più variabili a dei valori spacchettati all'interno di una sola istruzione. Questo permette di avere un codice più conciso e leggibile, e si ottiene separando con una virgola le variabili da assegnare, ad esempio first, second, third = (1,2,3) oppure for index, item in enumerate(iterable).

Gli operatori speciali * e ** vengono spesso usati in contesti di spacchettamento. * può essere usato per combinare più list/tuple in un'unica list/tuple spacchettando ognuna di esse in una nuova list/tuple comune. ** può essere usato per combinare più dizionari in un unico dizionario spacchettando ognuno di essi in un nuovo dict comune.

Quando l'operatore * viene usato senza una collezione, impacchetta una serie di valori in una list. Questo viene spesso usato nell'assegnazione multipla per raggruppare in un'unica variabile tutti gli elementi «rimanenti» che non hanno un'assegnazione individuale.

In Python è comune sfruttare questo comportamento di spacchettamento/impacchettamento anche quando si usano o si definiscono funzioni che accettano un numero arbitrario di argomenti posizionali o keyword. Spesso vedrai questi parametri «speciali» definiti come def some_function(*args, **kwargs) e gli argomenti «speciali» usati come some_function(*some_tuple, **some_dict).

Caution

*<variable_name> e **<variable_name> non vanno confusi con * e **. Mentre * e ** sono usati rispettivamente per la moltiplicazione e l'elevamento a potenza, *<variable_name> e **<variable_name> sono usati come operatori di impacchettamento e spacchettamento.

Assegnazione multipla

Nell'assegnazione multipla, il numero di variabili a sinistra dell'operatore di assegnazione (=) deve corrispondere al numero di valori a destra. Per separare i valori, usa una virgola ,:

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

Se l'assegnazione multipla riceve un numero di variabili non corretto rispetto ai valori forniti, viene generato un ValueError:

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

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

L'assegnazione multipla non è limitata a un solo tipo di dato:

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

>>> y
'Hello'

>>> z
True

L'assegnazione multipla può essere usata per scambiare gli elementi in lists. Questa pratica è piuttosto comune negli algoritmi di ordinamento. Ad esempio:

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

Poiché le tuple sono immutabili, non puoi scambiare elementi all'interno di una tuple.

Spacchettamento

Note

Gli esempi seguenti usano lists, ma gli stessi concetti valgono per le tuples.

In Python è possibile spacchettare gli elementi di una list/tuple/dict in variabili distinte. Poiché i valori compaiono all'interno di lists/tuples in un ordine specifico, vengono spacchettati in variabili nello stesso ordine:

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

Se ci sono valori che non servono, puoi usare _ per segnalarli:

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

Spacchettamento profondo

Spacchettare e assegnare valori da una list/tuple annidata dentro una list o una tuple (ovvero array e tuple annidati) funziona allo stesso modo di uno spacchettamento superficiale, ma spesso richiede dei qualificatori per chiarire il contesto o la posizione del valore:

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

>>> d
"potato"

Puoi anche spacchettare in profondità solo una porzione di una list/tuple annidata:

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

>>> c
"carrot"

Se lo spacchettamento ha variabili posizionate in modo errato e/o un numero errato di valori, otterrai un ValueError:

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

ValueError: too many values to unpack (expected 1)

Spacchettare un array/tuple con *

Quando spacchetti una list/tuple puoi usare l'operatore * per catturare i valori «rimanenti». Questo è più chiaro rispetto a fare slicing di una list/tuple (che in alcune situazioni è meno leggibile). Ad esempio, possiamo estrarre il primo elemento e impacchettare i valori rimanenti in una nuova list senza il primo elemento:

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

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

Possiamo anche estrarre i valori all'inizio e alla fine della list raggruppando tutti i valori centrali:

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

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

Possiamo anche usare * nello spacchettamento profondo:

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

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

Spacchettare un dizionario

Spacchettare un dizionario è un po' diverso dallo spacchettare una list/tuple. L'iterazione sui dizionari avviene di default sulle chiavi. Quindi, quando spacchetti un dict, puoi spacchettare solo le chiavi e non i valori:

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

Se vuoi spacchettare i valori, puoi usare il metodo <dict>.values():

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

Se servono sia le chiavi che i valori, usa il metodo <dict>.items(). <dict>.items() genera una vista iterabile che contiene coppie chiave-valore. Queste possono essere spacchettate in una tuple:

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

Impacchettamento

L'impacchettamento è la capacità di raggruppare più valori in un'unica list assegnata a una variabile. Questo è utile quando vuoi spacchettare dei valori, apportare delle modifiche e poi impacchettare di nuovo i risultati in una variabile. Rende anche possibile eseguire fusioni tra 2 o più lists/tuples/dicts.

Impacchettare un array/tuple con *

Impacchettare una list/tuple si può fare usando l'operatore *. Questo impacchetta tutti i valori in una 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']

Per maggiori dettagli sull'uso di * e **, dai un'occhiata alla PEP 3132 e alla PEP 448.

Impacchettare un dizionario con **

Impacchettare un dizionario si fa usando l'operatore **. Questo impacchetta tutte le coppie chiave-valore da un dizionario in un altro dizionario, oppure combina due dizionari insieme.

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

Uso di * e ** con le funzioni

Impacchettare con i parametri di una funzione

Quando crei una funzione che accetta un numero arbitrario di argomenti, puoi usare *args o **kwargs nella definizione della funzione. *args viene usato per impacchettare un numero arbitrario di argomenti posizionali (non keyword) e **kwargs viene usato per impacchettare un numero arbitrario di argomenti keyword.

Uso di *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")

Uso di **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 e **kwargs possono anche essere usati in combinazione tra loro:

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

Puoi anche scrivere dei parametri prima di *args per consentire argomenti posizionali specifici. I singoli argomenti keyword devono poi comparire prima di **kwargs.

Caution

Gli argomenti devono essere strutturati così:

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

Se non segui questo ordine, otterrai un errore.

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

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

Scrivere gli argomenti in un ordine errato provocherà un errore:

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

Spacchettare nelle chiamate di funzione

Puoi usare * per spacchettare una list/tuple di argomenti in una chiamata di funzione. Questo è molto utile per le funzioni che non accettano un iterable:

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

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

Usare lo spacchettamento con * insieme alla funzione integrata zip() è un altro caso d'uso comune. La funzione zip() prende più iterabili e restituisce una list di tuple con i valori di ciascun iterable raggruppati:

>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Unpacking e assegnazione multipla

La pratica è bloccata

Sblocca 3 altri esercizi per esercitarti su Unpacking e assegnazione multipla