5. Strutture Dati
*****************

Questo capitolo descrive alcune cose che hai già imparato in maggiore
dettaglio, e aggiunge anche alcune nuove cose.


5.1. Più sulle Liste
====================

The list data type has some more methods. Here are all of the methods
of list objects:

list.append(value, /)

   Add an item to the end of the list.  Similar to "a[len(a):] = [x]".

list.extend(iterable, /)

   Extend the list by appending all the items from the iterable.
   Similar to "a[len(a):] = iterable".

list.insert(index, value, /)

   Inserisce un elemento in una posizione specificata. Il primo
   argomento è l'indice dell'elemento prima del quale inserire, quindi
   "a.insert(0, x)" inserisce all'inizio della lista, e
   "a.insert(len(a), x)" è equivalente a "a.append(x)".

list.remove(value, /)

   Remove the first item from the list whose value is equal to
   *value*.  It raises a "ValueError" if there is no such item.

list.pop(index=-1, /)

   Rimuove l'elemento nella posizione specificata nella lista e lo
   restituisce. Se non viene specificato un indice, "a.pop()" rimuove
   e restituisce l'ultimo elemento della lista. Solleva un
   "IndexError" se la lista è vuota o l'indice è fuori dal range della
   lista.

list.clear()

   Remove all items from the list.  Similar to "del a[:]".

list.index(value[, start[, stop]])

   Return zero-based index of the first occurrence of *value* in the
   list. Raises a "ValueError" if there is no such item.

   Gli argomenti opzionali *start* e *end* sono interpretati come
   nella notazione delle slice e sono usati per limitare la ricerca a
   una particolare sottosequenza della lista. L'indice restituito è
   calcolato rispetto all'inizio della sequenza completa piuttosto che
   all'argomento *start*.

list.count(value, /)

   Return the number of times *value* appears in the list.

list.sort(*, key=None, reverse=False)

   Ordina gli elementi della lista in loco (gli argomenti possono
   essere usati per la personalizzazione dell'ordinamento, vedere
   "sorted()" per la loro spiegazione).

list.reverse()

   Inverte gli elementi della lista in loco.

list.copy()

   Return a shallow copy of the list.  Similar to "a[:]".

Un esempio che utilizza la maggior parte dei metodi della lista:

   >>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
   >>> fruits.count('apple')
   2
   >>> fruits.count('tangerine')
   0
   >>> fruits.index('banana')
   3
   >>> fruits.index('banana', 4)  # Find next banana starting at position 4
   6
   >>> fruits.reverse()
   >>> fruits
   ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
   >>> fruits.append('grape')
   >>> fruits
   ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
   >>> fruits.sort()
   >>> fruits
   ['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
   >>> fruits.pop()
   'pear'

Avrai notato che metodi come "insert", "remove" o "sort" che
modificano solo la lista non stampano alcun valore di ritorno --
ritornano il default "None". [1] Questo è un principio di design per
tutte le strutture dati mutabili in Python.

Un'altra cosa che potresti notare è che non tutti i dati possono
essere ordinati o confrontati. Per esempio, "[None, 'hello', 10]" non
può essere ordinata perché gli interi non possono essere confrontati
con le stringhe e "None" non può essere confrontato con altri tipi.
Inoltre, ci sono alcuni tipi che non hanno una relazione di
ordinamento definita. Ad esempio, "3+4j < 5+7j" non è un confronto
valido.


5.1.1. Usare le Liste come Pile
-------------------------------

I metodi delle liste rendono molto facile usare una lista come una
pila, dove l'ultimo elemento aggiunto è il primo elemento recuperato
("last-in, first-out"). Per aggiungere un elemento alla cima della
pila, usa "append()". Per recuperare un elemento dalla cima della
pila, usa "pop()" senza un indice esplicito. Per esempio:

   >>> stack = [3, 4, 5]
   >>> stack.append(6)
   >>> stack.append(7)
   >>> stack
   [3, 4, 5, 6, 7]
   >>> stack.pop()
   7
   >>> stack
   [3, 4, 5, 6]
   >>> stack.pop()
   6
   >>> stack.pop()
   5
   >>> stack
   [3, 4]


5.1.2. Usare le Liste come Code
-------------------------------

È anche possibile usare una lista come una coda, dove il primo
elemento aggiunto è il primo elemento recuperato ("first-in, first-
out"); tuttavia, le liste non sono efficienti per questo scopo. Mentre
gli append e i pop dalla fine della lista sono veloci, fare inserzioni
o pop dall'inizio di una lista è lento (perché tutti gli altri
elementi devono essere spostati di uno).

Per implementare una coda, usa "collections.deque" che è stata
progettata per avere append e pop veloci da entrambi i lati. Per
esempio:

   >>> from collections import deque
   >>> queue = deque(["Eric", "John", "Michael"])
   >>> queue.append("Terry")           # Terry arrives
   >>> queue.append("Graham")          # Graham arrives
   >>> queue.popleft()                 # The first to arrive now leaves
   'Eric'
   >>> queue.popleft()                 # The second to arrive now leaves
   'John'
   >>> queue                           # Remaining queue in order of arrival
   deque(['Michael', 'Terry', 'Graham'])


5.1.3. Comprensioni di Lista
----------------------------

Le comprensioni di lista forniscono un modo conciso per creare liste.
Le applicazioni comuni sono creare nuove liste dove ogni elemento è il
risultato di alcune operazioni applicate a ciascun membro di un'altra
sequenza o iterabile, o creare una sottosequenza di quegli elementi
che soddisfano una certa condizione.

Per esempio, supponiamo di voler creare una lista di quadrati, come:

   >>> squares = []
   >>> for x in range(10):
   ...     squares.append(x**2)
   ...
   >>> squares
   [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Nota che questo crea (o sovrascrive) una variabile chiamata "x" che
esiste ancora dopo che il ciclo è completo. Possiamo calcolare la
lista dei quadrati senza effetti collaterali usando:

   squares = list(map(lambda x: x**2, range(10)))

oppure, equivalentemente:

   squares = [x**2 for x in range(10)]

il che è più conciso e leggibile.

Una comprensione di lista consiste di parentesi contenenti
un'espressione seguita da una clausola "for", quindi zero o più
clausole "for" o "if". Il risultato sarà una nuova lista risultante
dalla valutazione dell'espressione nel contesto delle clausole "for" e
"if" che la seguono. Per esempio, questa listcomp combina gli elementi
di due liste se non sono uguali:

   >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
   [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

e equivale a:

   >>> combs = []
   >>> for x in [1,2,3]:
   ...     for y in [3,1,4]:
   ...         if x != y:
   ...             combs.append((x, y))
   ...
   >>> combs
   [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Nota come l'ordine delle istruzioni "for" e "if" sia lo stesso in
entrambi questi snippet.

Se l'espressione è una tupla (per es. il "(x, y)" nell'esempio
precedente), deve essere racchiusa tra parentesi.

   >>> vec = [-4, -2, 0, 2, 4]
   >>> # create a new list with the values doubled
   >>> [x*2 for x in vec]
   [-8, -4, 0, 4, 8]
   >>> # filter the list to exclude negative numbers
   >>> [x for x in vec if x >= 0]
   [0, 2, 4]
   >>> # apply a function to all the elements
   >>> [abs(x) for x in vec]
   [4, 2, 0, 2, 4]
   >>> # call a method on each element
   >>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
   >>> [weapon.strip() for weapon in freshfruit]
   ['banana', 'loganberry', 'passion fruit']
   >>> # create a list of 2-tuples like (number, square)
   >>> [(x, x**2) for x in range(6)]
   [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
   >>> # the tuple must be parenthesized, otherwise an error is raised
   >>> [x, x**2 for x in range(6)]
     File "<stdin>", line 1
       [x, x**2 for x in range(6)]
        ^^^^^^^
   SyntaxError: did you forget parentheses around the comprehension target?
   >>> # flatten a list using a listcomp with two 'for'
   >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
   >>> [num for elem in vec for num in elem]
   [1, 2, 3, 4, 5, 6, 7, 8, 9]

Le comprensioni di lista possono contenere espressioni complesse e
funzioni nidificate:

   >>> from math import pi
   >>> [str(round(pi, i)) for i in range(1, 6)]
   ['3.1', '3.14', '3.142', '3.1416', '3.14159']


5.1.4. Comprensioni di Lista Nidificate
---------------------------------------

L'espressione iniziale in una comprensione di lista può essere
qualsiasi espressione arbitraria, inclusa un'altra comprensione di
lista.

Considera il seguente esempio di una matrice 3x4 implementata come una
lista di 3 liste di lunghezza 4:

   >>> matrix = [
   ...     [1, 2, 3, 4],
   ...     [5, 6, 7, 8],
   ...     [9, 10, 11, 12],
   ... ]

La seguente comprensione di lista trasporrà righe e colonne:

   >>> [[row[i] for row in matrix] for i in range(4)]
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Come abbiamo visto nella sezione precedente, la comprensione di lista
interna è valutata nel contesto del "for" che la segue, quindi questo
esempio è equivalente a:

   >>> transposed = []
   >>> for i in range(4):
   ...     transposed.append([row[i] for row in matrix])
   ...
   >>> transposed
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

che, a sua volta, è lo stesso di:

   >>> transposed = []
   >>> for i in range(4):
   ...     # the following 3 lines implement the nested listcomp
   ...     transposed_row = []
   ...     for row in matrix:
   ...         transposed_row.append(row[i])
   ...     transposed.append(transposed_row)
   ...
   >>> transposed
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Nel mondo reale, dovresti preferire le funzioni built-in a strutture
di controllo complesse. La funzione "zip()" farebbe un ottimo lavoro
per questo caso d'uso:

   >>> list(zip(*matrix))
   [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

Vedi Spacchettamento delle Liste di Argomenti per dettagli
sull'asterisco in questa riga.


5.1.5. Unpacking in Lists and List Comprehensions
-------------------------------------------------

The section on Spacchettamento delle Liste di Argomenti describes the
use of "*" to "unpack" the elements of an iterable object, providing
each one separately as an argument to a function.  Unpacking can also
be used in other contexts, for example, when creating lists.  When
specifying elements of a list, prefixing an expression by a "*" will
unpack the result of that expression, adding each of its elements to
the list we're creating:

   >>> x = [1, 2, 3]
   >>> [0, *x, 4, 5, 6]
   [0, 1, 2, 3, 4, 5, 6]

This only works if the expression following the "*" evaluates to an
iterable object; trying to unpack a non-iterable object will raise an
exception:

   >>> x = 1
   >>> [0, *x, 2, 3, 4]
   Traceback (most recent call last):
     File "<python-input-1>", line 1, in <module>
       [0, *x, 2, 3, 4]
   TypeError: Value after * must be an iterable, not int

Unpacking can also be used in list comprehensions, as a way to build a
new list representing the concatenation of an arbitrary number of
iterables:

   >>> x = [[1, 2, 3], [4, 5, 6], [], [7], [8, 9]]
   >>> [*element for element in x]
   [1, 2, 3, 4, 5, 6, 7, 8, 9]

Note that the effect is that each element from "x" is unpacked.  This
works for arbitrary iterable objects, not just lists:

   >>> x = [[1, 2, 3], 'cat', {'spam': 'eggs'}]
   >>> [*element for element in x]
   [1, 2, 3, 'c', 'a', 't', 'spam']

But if the objects in "x" are not iterable, this expression would
again raise an exception.


5.2. L'istruzione "del"
=======================

Esiste un modo per rimuovere un elemento da una lista dato il suo
indice invece del suo valore: l'istruzione "del". Questo differisce
dal metodo "pop()" che restituisce un valore. L'istruzione "del" può
essere usata anche per rimuovere slice da una lista o per cancellare
l'intera lista (cosa che abbiamo fatto prima assegnando una lista
vuota alla slice). Per esempio:

   >>> a = [-1, 1, 66.25, 333, 333, 1234.5]
   >>> del a[0]
   >>> a
   [1, 66.25, 333, 333, 1234.5]
   >>> del a[2:4]
   >>> a
   [1, 66.25, 1234.5]
   >>> del a[:]
   >>> a
   []

"del" può anche essere usato per cancellare variabili intere:

   >>> del a

Fare riferimento al nome "a" d'ora in poi è un errore (almeno finché
un altro valore non viene assegnato ad esso). Troveremo altri usi per
"del" più avanti.


5.3. Tuple e Sequenze
=====================

Abbiamo visto che liste e stringhe hanno molte proprietà comuni, come
le operazioni di indicizzazione e slicing. Sono due esempi di tipi di
dati *sequenza* (vedi Tipi di Sequenza --- list, tuple, range). Poiché
Python è un linguaggio in evoluzione, possono essere aggiunti altri
tipi di dati sequenza. Esiste anche un altro tipo di dati sequenza
standard: la *tupla*.

Una tupla consiste in un numero di valori separati da virgole, ad
esempio:

   >>> t = 12345, 54321, 'hello!'
   >>> t[0]
   12345
   >>> t
   (12345, 54321, 'hello!')
   >>> # Tuples may be nested:
   >>> u = t, (1, 2, 3, 4, 5)
   >>> u
   ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
   >>> # Tuples are immutable:
   >>> t[0] = 88888
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: 'tuple' object does not support item assignment
   >>> # but they can contain mutable objects:
   >>> v = ([1, 2, 3], [3, 2, 1])
   >>> v
   ([1, 2, 3], [3, 2, 1])
   >>> # they support unpacking just like lists:
   >>> x = [1, 2, 3]
   >>> 0, *x, 4
   (0, 1, 2, 3, 4)

Come vedi, in uscita le tuple sono sempre racchiuse tra parentesi, in
modo che le tuple annidate siano interpretate correttamente; possono
essere inserite con o senza parentesi, anche se spesso le parentesi
sono comunque necessarie (se la tupla fa parte di un'espressione più
grande). Tuttavia, non è possibile assegnare ai singoli elementi di
una tupla, mentre è possibile creare tuple che contengono oggetti
mutabili, come le liste.

Sebbene le tuple possano sembrare simili alle liste, vengono spesso
utilizzate in situazioni diverse e per scopi diversi. Le tuple sono
*immutable*, e di solito contengono una sequenza eterogenea di
elementi che sono accessibili tramite unpacking (vedi più avanti in
questa sezione) o indicizzazione (o anche per attributo nel caso di
"namedtuples"). Le liste sono *mutable*, e i loro elementi sono di
solito omogenei e sono accessibili iterando sulla lista.

Un problema speciale è la costruzione di tuple contenenti 0 o 1
elemento: la sintassi presenta alcune peculiarità per accogliere
questi casi. Le tuple vuote sono costruite da una coppia di parentesi
vuota; una tupla con un solo elemento è costruita seguendo un valore
con una virgola (non è sufficiente racchiudere un singolo valore tra
parentesi). Brutto, ma efficace. Ad esempio:

   >>> empty = ()
   >>> singleton = 'hello',    # <-- note trailing comma
   >>> len(empty)
   0
   >>> len(singleton)
   1
   >>> singleton
   ('hello',)

L'istruzione "t = 12345, 54321, 'hello!'" è un esempio di *tuple
packing*: i valori "12345", "54321" e "'hello!'" sono racchiusi
insieme in una tupla. L'operazione inversa è anche possibile:

   >>> x, y, z = t

Questo è chiamato, appropriatamente, *sequence unpacking* e funziona
per qualsiasi sequenza sul lato destro. Lo sequence unpacking richiede
che ci siano tante variabili sul lato sinistro del segno di uguale
quante sono gli elementi nella sequenza. Nota che l'assegnazione
multipla è davvero solo una combinazione di tuple packing e sequence
unpacking.


5.4. Insiemi
============

Python also includes a data type for sets.  A set is an unordered
collection with no duplicate elements.  Basic uses include membership
testing and eliminating duplicate entries.  Set objects also support
mathematical operations like union, intersection, difference, and
symmetric difference.

Le parentesi graffe o la funzione "set()" possono essere utilizzate
per creare insiemi. Nota: per creare un insieme vuoto devi utilizzare
"set()", non "{}"; quest'ultimo crea un dizionario vuoto, una
struttura dati che discuteremo nella sezione successiva.

Because sets are unordered, iterating over them or printing them can
produce the elements in a different order than you expect.

Ecco una breve dimostrazione:

   >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
   >>> print(basket)                      # show that duplicates have been removed
   {'orange', 'banana', 'pear', 'apple'}
   >>> 'orange' in basket                 # fast membership testing
   True
   >>> 'crabgrass' in basket
   False

   >>> # Demonstrate set operations on unique letters from two words
   >>>
   >>> a = set('abracadabra')
   >>> b = set('alacazam')
   >>> a                                  # unique letters in a
   {'a', 'r', 'b', 'c', 'd'}
   >>> a - b                              # letters in a but not in b
   {'r', 'd', 'b'}
   >>> a | b                              # letters in a or b or both
   {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
   >>> a & b                              # letters in both a and b
   {'a', 'c'}
   >>> a ^ b                              # letters in a or b but not both
   {'r', 'd', 'b', 'm', 'z', 'l'}

Similarly to list comprehensions, set comprehensions are also
supported, including comprehensions with unpacking:

   >>> a = {x for x in 'abracadabra' if x not in 'abc'}
   >>> a
   {'r', 'd'}

   >>> fruits = [{'apple', 'avocado', 'apricot'}, {'banana', 'blueberry'}]
   >>> {*fruit for fruit in fruits}
   {'blueberry', 'banana', 'avocado', 'apple', 'apricot'}


5.5. Dizionari
==============

Un altro tipo di dato utile incorporato in Python è il *dizionario*
(vedi Mapping types --- dict, frozendict). I dizionari sono a volte
conosciuti in altre lingue come "memorie associative" o "array
associativi". A differenza delle sequenze, che sono indicizzate da un
intervallo di numeri, i dizionari sono indicizzati da *chiavi*, che
possono essere di qualsiasi tipo immutabile; stringhe e numeri possono
sempre essere chiavi. Le tuple possono essere usate come chiavi se
contengono solo stringhe, numeri o tuple; se una tupla contiene un
qualsiasi oggetto mutabile direttamente o indirettamente, non può
essere utilizzata come chiave. Non puoi usare le liste come chiavi,
poiché le liste possono essere modificate in loco usando assegnazioni
di indice, assegnazioni di slice o metodi come "append()" e
"extend()".

È meglio pensare a un dizionario come a un insieme di coppie *chiave:
valore*, con il requisito che le chiavi siano uniche (all'interno di
un dizionario). Una coppia di parentesi crea un dizionario vuoto:
"{}". Inserendo una lista di coppie chiave:valore separate da virgole
dentro le parentesi si aggiungono le coppie iniziali al dizionario;
questo è anche il modo in cui i dizionari vengono scritti in uscita.

The main operations on a dictionary are storing a value with some key
and extracting the value given the key.  It is also possible to delete
a key:value pair with "del". If you store using a key that is already
in use, the old value associated with that key is forgotten.

Extracting a value for a non-existent key by subscripting ("d[key]")
raises a "KeyError". To avoid getting this error when trying to access
a possibly non-existent key, use the "get()" method instead, which
returns "None" (or a specified default value) if the key is not in the
dictionary.

Eseguendo "list(d)" su un dizionario si ottiene una lista di tutte le
chiavi utilizzate nel dizionario, in ordine di inserimento (se la vuoi
ordinata, basta usare "sorted(d)"). Per controllare se una singola
chiave è nel dizionario, usa la parola chiave "in".

Ecco un piccolo esempio usando un dizionario:

   >>> tel = {'jack': 4098, 'sape': 4139}
   >>> tel['guido'] = 4127
   >>> tel
   {'jack': 4098, 'sape': 4139, 'guido': 4127}
   >>> tel['jack']
   4098
   >>> tel['irv']
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   KeyError: 'irv'
   >>> print(tel.get('irv'))
   None
   >>> del tel['sape']
   >>> tel['irv'] = 4127
   >>> tel
   {'jack': 4098, 'guido': 4127, 'irv': 4127}
   >>> list(tel)
   ['jack', 'guido', 'irv']
   >>> sorted(tel)
   ['guido', 'irv', 'jack']
   >>> 'guido' in tel
   True
   >>> 'jack' not in tel
   False

Il costruttore "dict()" costruisce dizionari direttamente da sequenze
di coppie chiave-valore:

   >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
   {'sape': 4139, 'guido': 4127, 'jack': 4098}

Inoltre, le dict comprehensions possono essere utilizzate per creare
dizionari da espressioni di chiave e valore arbitrarie:

   >>> {x: x**2 for x in (2, 4, 6)}
   {2: 4, 4: 16, 6: 36}

And dictionary unpacking (via "**") can be used to merge multiple
dictionaries:

   >>> odds = {i: i**2 for i in (1, 3, 5)}
   >>> evens = {i: i**2 for i in (2, 4, 6)}
   >>> {**odds, **evens}
   {1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36}

   >>> all_values = [odds, evens, {0: 0}]
   >>> {**i for i in all_values}
   {1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36, 0: 0}

Quando le chiavi sono semplici stringhe, a volte è più facile
specificare le coppie utilizzando gli argomenti per parola chiave:

   >>> dict(sape=4139, guido=4127, jack=4098)
   {'sape': 4139, 'guido': 4127, 'jack': 4098}


5.6. Tecniche di Looping
========================

Quando si scorre un dizionario, è possibile recuperare
contemporaneamente la chiave e il valore corrispondente usando il
metodo "items()".

   >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
   >>> for k, v in knights.items():
   ...     print(k, v)
   ...
   gallahad the pure
   robin the brave

Quando si scorre una sequenza, è possibile recuperare
contemporaneamente l'indice di posizione e il valore corrispondente
usando la funzione "enumerate()".

   >>> for i, v in enumerate(['tic', 'tac', 'toe']):
   ...     print(i, v)
   ...
   0 tic
   1 tac
   2 toe

Per scorrere due o più sequenze contemporaneamente, le voci possono
essere abbinate con la funzione "zip()".

   >>> questions = ['name', 'quest', 'favorite color']
   >>> answers = ['lancelot', 'the holy grail', 'blue']
   >>> for q, a in zip(questions, answers):
   ...     print('What is your {0}?  It is {1}.'.format(q, a))
   ...
   What is your name?  It is lancelot.
   What is your quest?  It is the holy grail.
   What is your favorite color?  It is blue.

Per scorrere una sequenza al contrario, specifica prima la sequenza in
direzione avanti e poi chiama la funzione "reversed()".

   >>> for i in reversed(range(1, 10, 2)):
   ...     print(i)
   ...
   9
   7
   5
   3
   1

Per scorrere una sequenza in ordine ordinato, usa la funzione
"sorted()" che restituisce una nuova lista ordinata lasciando
invariata la sorgente.

   >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
   >>> for i in sorted(basket):
   ...     print(i)
   ...
   apple
   apple
   banana
   orange
   orange
   pear

Usare "set()" su una sequenza elimina gli elementi duplicati. L'uso di
"sorted()" in combinazione con "set()" su una sequenza è un modo
idiomatico di scorrere gli elementi unici della sequenza in ordine
ordinato.

   >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
   >>> for f in sorted(set(basket)):
   ...     print(f)
   ...
   apple
   banana
   orange
   pear

A volte è allettante modificare una lista mentre la stai scorrendo;
tuttavia, è spesso più semplice e sicuro creare una nuova lista
invece.

   >>> import math
   >>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
   >>> filtered_data = []
   >>> for value in raw_data:
   ...     if not math.isnan(value):
   ...         filtered_data.append(value)
   ...
   >>> filtered_data
   [56.2, 51.7, 55.3, 52.5, 47.8]


5.7. Maggiori informazioni sulle Condizioni
===========================================

Le condizioni utilizzate nelle istruzioni "while" e "if" possono
contenere qualsiasi operatore, non solo confronti.

Gli operatori di confronto "in" e "not in" sono test di appartenenza
che determinano se un valore è (o non è) in un contenitore.  Gli
operatori "is" e "is not" confrontano se due oggetti sono davvero lo
stesso oggetto.  Tutti gli operatori di confronto hanno la stessa
priorità, che è inferiore a quella di tutti gli operatori numerici.

I confronti possono essere concatenati.  Ad esempio, "a < b == c"
verifica se "a" è minore di "b" e inoltre se "b" è uguale a "c".

I confronti possono essere combinati usando gli operatori booleani
"and" e "or", e il risultato di un confronto (o di qualsiasi altra
espressione booleana) può essere negato con "not".  Questi hanno una
priorità inferiore rispetto agli operatori di confronto; tra di loro,
"not" ha la priorità più alta e "or" la più bassa, in modo che "A and
not B or C" sia equivalente a "(A and (not B)) or C". Come sempre, le
parentesi possono essere utilizzate per esprimere la composizione
desiderata.

Gli operatori booleani "and" e "or" sono i cosiddetti operatori
*short-circuit*: i loro argomenti vengono valutati da sinistra a
destra, e la valutazione si arresta non appena l'esito è determinato.
Ad esempio, se "A" e "C" sono veri ma "B" è falso, "A and B and C" non
valuta l'espressione "C".  Quando vengono utilizzati come un valore
generale e non come booleani, il valore restituito di un operatore
short-circuit è l'ultimo argomento valutato.

È possibile assegnare il risultato di un confronto o di un'altra
espressione booleana a una variabile.  Ad esempio,

   >>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
   >>> non_null = string1 or string2 or string3
   >>> non_null
   'Trondheim'

Nota che in Python, a differenza del C, l'assegnazione dentro
espressioni deve essere fatta esplicitamente con l'operatore detto
walrus operator ":=". Questo evita una comune classe di problemi
incontrati nei programmi C: digitare "=" in un'espressione quando si
intendeva "==".


5.8. Confronto tra Sequenze e Altri Tipi
========================================

Gli oggetti sequenza possono essere tipicamente confrontati con altri
oggetti dello stesso tipo di sequenza. Il confronto usa l'ordinamento
*lessicografico*: prima vengono confrontati i primi due elementi, e se
differiscono questo determina il risultato del confronto; se sono
uguali, vengono confrontati i due successivi, e così via, fino a
quando una delle due sequenze è esaurita. Se gli elementi da
confrontare sono essi stessi sequenze dello stesso tipo, il confronto
lessicografico viene effettuato ricorsivamente.  Se tutti gli elementi
di due sequenze sono uguali tra di loro, le sequenze sono considerate
uguali. Se una sequenza è una sottosequenza iniziale dell'altra, la
sequenza più corta è considerata la minore.  L'ordinamento
lessicografico per le stringhe utilizza il numero del punto di codice
Unicode per ordinare i singoli caratteri. Alcuni esempi di confronti
tra sequenze dello stesso tipo:

   (1, 2, 3)              < (1, 2, 4)
   [1, 2, 3]              < [1, 2, 4]
   'ABC' < 'C' < 'Pascal' < 'Python'
   (1, 2, 3, 4)           < (1, 2, 4)
   (1, 2)                 < (1, 2, -1)
   (1, 2, 3)             == (1.0, 2.0, 3.0)
   (1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

Nota che confrontare oggetti di tipi diversi con "<" o ">" è legale a
condizione che gli oggetti abbiano metodi di confronto appropriati.
Ad esempio, i tipi numerici misti sono confrontati in base al loro
valore numerico, quindi 0 è uguale a 0.0, ecc.  Altrimenti, invece di
fornire un ordinamento arbitrario, l'interprete alzerà un'eccezione
"TypeError".

-[ Note a piè di pagina ]-

[1] Altri linguaggi possono restituire l'oggetto mutato, il che
    consente di concatenare i metodi, come
    "d->insert("a")->remove("b")->sort();".
