8.3. Filtrare liste rivisitate

Avete già familiarità con l'utilizzo delle list comprehensions per filtrare le liste. C'è un'altro modo per raggiungere lo stesso risultato, che alcune persone considerano più espressivo.

Python ha una funzione built-in chiamata filter che prende due argomenti, una funzione ed una lista, e ritorna una lista. [14] La funzione passata come primo argomento a filter deve essa stessa prendere un argomento, e la lista che filter ritorna conterrà tutti gli elementi dalla lista passati a filter per i quali la funzione elaborata ritorna true.

Capito tutto? Non è difficile come sembra.

Esempio 8.7. Introdurre filter

>>> def odd(n):           1
...     return n%2
...     
>>> li = [1, 2, 3, 5, 9, 10, 256, -3]
>>> filter(odd, li)       2
[1, 3, 5, 9, -3]
>>> filteredList = []
>>> for n in li:          3
...     if odd(n):
...         filteredList.append(n)
...     
>>> filteredList
[1, 3, 5, 9, -3]
1 odd usa la funzione built-in mod “%” per ritornare 1 se n è dispari e 0 se n è pari.
2 filter prende due argomenti, una funzione (odd) e una lista (li). Esegue un ciclo nella lista e chiama odd con ogni elemento. Se odd ritorna un valore vero (ricordate, ogni valore diverso da zero è vero in Python), l'elemento è incluso nella lista ritornata, altrimenti ne è fuori, nello stesso ordine in cui appare nell'originale.
3 Potreste raggiungere lo stesso risultato con un ciclo for. A seconda del vostro stile di programmazione, ciò può sembrare più “diretto”, ma funzioni come filter sono molto più espressive. Non solo sono più facili da scrivere, ma anche da leggere. Leggere il ciclo for è come stare troppo vicino ad un dipinto; vedete tutti i dettagli, ma ci vogliono alcuni secondi per fare un passo indietro e vedere l'intera opera: “Ehi, stavamo semplicemente filtrando una lista!”.

Esempio 8.8. filter in regression.py

    files = os.listdir(path)                                1
    test = re.compile("test\.py$", re.IGNORECASE)           2
    files = filter(test.search, files)                      3
1 Come abbiamo visto nella sezione Trovare il percorso, path può contenere il percorso completo o parziale della directory dello script corrente, o può contenere una stringa vuota se lo script è stato lanciato dalla directory corrente. D'altra parte, files finirà con i nomi dei file nella stessa directory dalla quale lo script è stato lanciato.
2 Questa è una espressione regolare compilata. Come abbiamo visto nella sezione Rifattorizzazione, se userete la stessa espressione regolare più e più volte, dovreste compilarla per una migliore performance. L'oggetto compile ha un metodo search che prende un singolo elemento, la stringa da cercare, se l'espressione regolare rispecchia la stringa. Il metodo search ritorna un oggetto Match contenente informazioni sul confronto dell'espressione regolare; altrimenti ritorna None, il valore nullo di Python.
3 Per ogni elemento nella lista files, chiameremo il metodo search dell'oggetto compilato dell'espressione regolare, test. Se l'espressione regolare coincide, il metodo ritornerà un oggetto Match, che Python considera essere vero, così l'elemento sarà incluso nella lista ritornata da filter. Se l'espressione regolare non coincide, il metodo search ritornerà None, che Python cosidera essere falso, così l'elemento non sarà incluso.

Nota storica.  Le versioni di Python anteriori alla 2.0 non hanno le list comprehensions, perciò non potevate filtrare usando le list comprehensions; la funzione filter era l'unica possibilità. Anche con l'introduzione delle list comprehensions dalla versione 2.0, alcune persone preferiscono ancora la vecchia filter (e la sua funzione gemella, map, che vedremo nella prossima sezione). Entrambe queste tecniche funzionano, e nessuna scomparirà, quindi è solo una questione di stile.

Esempio 8.9. Filtrare usando le list comprehensions

    files = os.listdir(path)                               
    test = re.compile("test\.py$", re.IGNORECASE)          
files = [f for f in files if test.search(f)] 1
1 Questo raggiungerà lo stesso risultato della funzione filter. Qual'è la più espressiva? A voi la scelta.

Footnotes

[14] Tecnicamente, il secondo argomento di filter può essere una qualsiasi sequenza, incluse liste, tuple e classi che si comportano come liste, definendo il metodo speciale ___getitem__. Se possibile, filter restituirà lo stesso tipo di dato che ha ricevuto, quindi filtrando una lista si ottiene una lista, invece filtrando una tupla si ottiene una tupla.