# 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();`.