Spacchettamento di sequenze

Introduzione

Lo spacchettamento di sequenze è un'operazione molto comune in Python e vale la pena di soffermarcisi un attimo per presentare qualche esempio.

Richiamo di una funzione

Immaginiamo di avere una funzione come la seguente:

def describe_person(name, age):
    assert(isinstance(name, str))
    assert(isinstance(age, int))
    print('La persona di nome %s ha %d anni' % (name, age))

Essa potrà essere richiamata in uno dei seguenti modi:

describe_person('mario', 32)
# chiamata con parametri nel giusto ordine

describe_person(age=32, name='mario')
# chiamata con parametri in forma chiave/valore
# si noti che l'ordine non è importante

info = ('mario', 32)
describe_person(*info)
# chiamata con i parametri in una tupla e "sequence unpacking"

info = ['mario', 32]
describe_person(*info)
# chiamata con i parametri in una lista e "sequence unpacking"

info = {'age': 32, 'name': 'mario'}
describe_person(**info)
# chiamata con i parametri in un dizionario e "dict unpacking"
# notare che in questo caso si mettono due asterischi

In ogni caso, i parametri ricevuti, e di conseguenza l'output, sarà il medesimo:

La persona di nome mario ha 32 anni

Numero arbitrario di parametri per una funzione

Immaginiamo di avere bisogno di avere un numero arbitrario di parametri per una funzione. Possiamo definirla in questo modo:

def do_something(*args):
    print('Elenco degli argomenti:')
    for v in args:
        print(v)
    print('Elenco degli argomenti (con indice numerico):')
    for i in range(len(args)):
        print(i, args[i])

if __name__=='__main__':

    do_something('uno', 'due', 'tre')

L'output sarà:

Elenco degli argomenti:
uno
due
tre
Elenco degli argomenti (con indice numerico):
0 uno
1 due
2 tre

Parametri in forma chiave/valore

Se vogliamo che la funzione ammetta parametri nella forma chiave/valore possiamo procedere in questo modo:

def do_something(*args, **kwargs):
    print('Elenco degli argomenti senza parola chiave:')
    print(args)
    for v in args:
        print(v)
    print('Elenco degli argomenti con parola chiave:')
    print(kwargs)
    for k,v in kwargs.items():
        print(k, '=', v)

if __name__=='__main__':

    do_something('uno', 'due', 'tre', valore=100, totale=200)

Output:

Elenco degli argomenti senza parola chiave:
('uno', 'due', 'tre')
uno
due
tre
Elenco degli argomenti con parola chiave:
{'totale': 200, 'valore': 100}
totale = 200
valore = 100

Uso di un dizionario per stringhe di formattazione

Può essere comodo usare un dizionario per impostare i valori dei segnaposto in una stringa di formattazione. Come visto in uno dei primi esempi in una precedente unità, potrei scrivere l'istruzione

print("L'utente si chiama {name} e ha {age} anni.".format(name='Giorgio', age=22)

per ottenere

L'utente si chiama Giorgio e ha 22 anni.

Il medesimo risultato può essere ottenuto con i valori presenti in un dizionario, spacchettato quando esso diventa argomento del metodo format della classe str:

dic={'name':'Giorgio', 'age':22}
print("L'utente si chiama {name} e ha {age} anni.".format(**dic))

Si noti che in questi esempi la stringa "L'utente si chiama {name} e ha {age} anni." è l'oggetto di cui chiamiamo il metodo format(). Avremmo potuto ovviamente assegnare la stringa ad una variabile e poi richiamare il metodo usando la variabile.

Spacchettamento parziale

Può essere necessario prelevare da una sequenza una parte degli elementi, come negli esempi che seguono.

a = ['arancia', 'mela', 'pera', 'fragola', 'banana', 'ananas']

primo, secondo, *altri = a
print('primo: ', primo)
print('secondo: ', secondo)
print('altri: ', altri)

primo: arancia
secondo: mela
altri: ['pera', 'fragola', 'banana', 'ananas']

primo, *centrali, ultimo = a
print('primo: ', primo)
print('centrali: ', centrali)
print('ultimo: ', ultimo)

primo: arancia
centrali: ['mela', 'pera', 'fragola', 'banana']
ultimo: ananas

Le stringhe sono sequenze, per cui il metodo funziona anche con esse:

nome="Mario"
primalettera,*successive = nome
print('prima lettera: ', primalettera)
print('successive: ', successive)

prima lettera: M
successive: ['a', 'r', 'i', 'o']

Spacchettamento di oggetti che implementano iteratori

Se una classe implementa un iteratore (in sintesi, ci permette di fare un ciclo for con i valori via via generati), è possibile spacchettare i valori delle sue istanze. L'esempio classico è quello della classe range:

>>> print(range(2,8))
range(2, 8)
>>> print(*range(2,8))
2 3 4 5 6 7

Ovviamente vale anche

>>> r=range(2,8)
>>> print(r)
range(2, 8)
>>> print(*r)
2 3 4 5 6 7

Come faccio a sapere se un dato oggetto è una sequenza?

Gli oggetti sono sequenze se implementano l'iteration protocol o il sequence protocol (in sostanza, devono definire alcuni metodi speciali, quali __iter__(), __next__(), ecc.). Per sapere se un oggetto è una sequenza, l'approccio più semplice è di vedere se con esso si può fare un'iterazione. Volendo, si può anche scrivere una funzione come la seguente:

def isIterable(object):
   try:
       k=iter(object)
       return True
   except TypeError:
       return False

e poi provarla con diversi tipi di oggetti:

>>> isIterable(2)
False
>>> isIterable('abc')
True
>>> isIterable('')
True
>>> isIterable((1,2,3))
True
>>> isIterable(open('/etc/services'))
True

results matching ""

    No results matching ""