10. Una breve visita alla libreria standard

10.1. Interfacce al sistema operativo

Il modulo os contiene moltissime funzioni per interagire con il sistema operativo:

>>> import os
>>> os.getcwd()      # restituisce la directory corrente
'C:\\Python38'
>>> os.chdir('/server/accesslogs')   # cambia la directory corrente
>>> os.system('mkdir today')   # esegue "mkdir" nella shell di sistema
0

È importante usare import os e non from os import *, in modo che la funzione os.open() non mascheri la funzione predefinita open() che lavora in modo molto differente.

È conveniente ricorrere alle funzioni predefinite dir() e help() per ricevere aiuto interattivo quando si lavora con moduli di grandi dimensioni come os:

>>> import os
>>> dir(os)
<restituisce una lista di tutte le funzioni del modulo>
>>> help(os)
<restituisce una documentazione completa costruita in base alle docstring>

Per il lavoro di tutti i giorni con file e directory, shutil fornisce un’interfaccia di livello più alto, che è più semplice da usare:

>>> import shutil
>>> shutil.copyfile('data.db', 'archive.db')
'archive.db'
>>> shutil.move('/build/executables', 'installdir')
'installdir'

10.2. Caratteri jolly per i file

Il modulo glob comprende una funzione per cercare file con i caratteri jolly:

>>> import glob
>>> glob.glob('*.py')
['primes.py', 'random.py', 'quote.py']

10.3. Argomenti della riga di comando

Molti script di hanno bisogno di processare gli argomenti della riga di comando. Questi argomenti vengono conservati nell’attributo argv del modulo sys, sotto forma di una lista. Per esempio, l’output che segue risulta dall’aver invocato python demo.py one two three al prompt della shell:

>>> import sys
>>> print(sys.argv)
['demo.py', 'one', 'two', 'three']

Il modulo argparse mette a disposizione un meccanismo più sofisticato per gestire gli argomenti della riga di comando. Lo script che segue estrae uno o più nomi di file e un numero opzionale di righe da visualizzare:

import argparse

parser = argparse.ArgumentParser(prog = 'top',
    description = 'Show top lines from each file')
parser.add_argument('filenames', nargs='+')
parser.add_argument('-l', '--lines', type=int, default=10)
args = parser.parse_args()
print(args)

Quando viene invocato con python top.py --lines=5 alpha.txt beta.txt, lo script imposta args.lines a 5 e args.filenames a ['alpha.txt', 'beta.txt'].

10.4. Re-dirigere lo standard error e terminare il programma

Il modulo sys ha degli attributi per stdin, stdout e stderr. Quest’ultimo è utile per emettere avvisi e messaggi d’errore e renderli visibili anche quando lo standard output è stato re-diretto:

>>> sys.stderr.write('Warning, log file not found starting a new one\n')
Warning, log file not found starting a new one

Il modo più diretto per terminare un programma è usare sys.exit().

10.5. Ricerca di pattern nelle stringhe

Il modulo re fornisce strumenti per il trattamento delle stringhe con le regular expression. Per ricerche e manipolazioni sofisticate, le regular expression costituiscono una soluzione compatta ed efficiente:

>>> import re
>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
'cat in the hat'

Tuttavia, per ricerche e sostituzioni semplici, è preferibile usare i metodi delle stringhe, che sono più semplici da leggere e correggere:

>>> 'tea for too'.replace('too', 'two')
'tea for two'

10.6. Matematica

Il modulo math dà accesso alla sottostante libreria C, che raccoglie funzioni per il calcolo in virgola mobile:

>>> import math
>>> math.cos(math.pi / 4)
0.70710678118654757
>>> math.log(1024, 2)
10.0

Il modulo random consente di effettuare selezioni casuali:

>>> import random
>>> random.choice(['apple', 'pear', 'banana'])
'apple'
>>> random.sample(range(100), 10)   # campionamento senza rimpiazzamento
[30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
>>> random.random()    # un float casuale
0.17970987693706186
>>> random.randrange(6)    # in intero casuale compreso in range(6)
4

Il modulo statistics produce misure statistiche di base (media, mediana, varianza etc.) su dati numerici:

>>> import statistics
>>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
>>> statistics.mean(data)
1.6071428571428572
>>> statistics.median(data)
1.25
>>> statistics.variance(data)
1.3720238095238095

Il progetto SciPy offre molti altri moduli per il calcolo numerico.

10.7. Accesso a internet

Esistono diversi moduli per accedere a internet e gestire i protocolli internet. Due dei più semplici sono urllib.request per raccogliere dati da una URL e smtplib per spedire email:

>>> from urllib.request import urlopen
>>> with urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl') as response:
...     for line in response:
...         line = line.decode('utf-8')  # converte i dati binari in testo
...         if 'EST' in line or 'EDT' in line:  # cerca EST
...             print(line)

<BR>Nov. 25, 09:43:32 PM EST

>>> import smtplib
>>> server = smtplib.SMTP('localhost')
>>> server.sendmail('soothsayer@example.org', 'jcaesar@example.org',
... """To: jcaesar@example.org
... From: soothsayer@example.org
...
... Beware the Ides of March.
... """)
>>> server.quit()

(Si noti che l’ultimo esempio richiede che un server mail sia funzionante su localhost.)

10.8. Date e orari

Il modulo datetime contiene delle classi per manipolazioni semplici e complesse di date e orari. Anche se i calcoli con le date sono supportati, il modulo si concentra soprattutto sull’estrazione dei componenti per scopi di manipolazione e formattazione. Sono anche previsti oggetti sensibili alle timezone.

>>> # dates are easily constructed and formatted
>>> from datetime import date
>>> now = date.today()
>>> now
datetime.date(2003, 12, 2)
>>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
'12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.'

>>> # le date supportano l'aritmetica del calendario
>>> birthday = date(1964, 7, 31)
>>> age = now - birthday
>>> age.days
14368

10.9. Compressione dei dati

I moduli zlib, gzip, bz2, lzma, zipfile e tarfile offrono il supporto per i comuni formati di archiviazione e compressione dei dati.

>>> import zlib
>>> s = b'witch which has which witches wrist watch'
>>> len(s)
41
>>> t = zlib.compress(s)
>>> len(t)
37
>>> zlib.decompress(t)
b'witch which has which witches wrist watch'
>>> zlib.crc32(s)
226805979

10.10. Misurazione di performance

Alcuni utenti di Python sono molto interessati a conoscere la differenza tra vari approcci allo stesso problema, in termini di performance. Python mette a disposizione uno strumento di misura che risponde immediatamente a queste domande.

Per esempio, si può provare a usare lo spacchettamento di tupla, invece del tradizionale approccio di scambiare le variabili. Il modulo timeit ci fa rapidamente vedere che in effetti esiste un leggero vantaggio di performance:

>>> from timeit import Timer
>>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
0.57535828626024577
>>> Timer('a,b = b,a', 'a=1; b=2').timeit()
0.54962537085770791

Mentre timeit ha un livello di granularità più fine, i moduli profile e pstats forniscono strumenti per identificare, all’interno di sezioni di codice più ampie, le parti che provocano rallentamenti.

10.11. Controllo di qualità

Una strada per scrivere codice di alta qualità è quella di scrivere dei test per ciascuna funzione, man mano che viene sviluppata, e di eseguire i test con una certa frequenza durante il processo di sviluppo.

Il modulo doctest è uno strumento per scansionare un modulo e validare i test che sono contenuti nelle sue docstring. Creare un test è questione di un semplice copia-e-incolla, nella docstring, dell’invocazione e del risultato atteso. In questo modo si migliora la documentazione, fornendo un esempio di utilizzo per l’utente, e si permette a doctest di garantire che il codice resti fedele a quanto documentato:

def average(values):
    """Restituisce la media aritmetica di una lista di numeri.

    >>> print(average([20, 30, 70]))
    40.0
    """
    return sum(values) / len(values)

import doctest
doctest.testmod()   # valida automaticamente i test inclusi

Il modulo unittest non è di immediato utilizzo come doctest, ma permette di mantenere una raccolta più completa di test in file separati:

import unittest

class TestStatisticalFunctions(unittest.TestCase):

    def test_average(self):
        self.assertEqual(average([20, 30, 70]), 40.0)
        self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
        with self.assertRaises(ZeroDivisionError):
            average([])
        with self.assertRaises(TypeError):
            average(20, 30, 70)

unittest.main()  # invocare dalla riga di comando esegue tutti i test

10.12. Le batterie sono incluse

La filosofia di Python è che «le batterie sono incluse». Ne è prova l’inclusione nella libreria standard di grandi package che forniscono strumenti più sofisticati e robusti. Per esempio:

  • Con i moduli xmlrpc.client e xmlrpc.server, realizzare invocazioni di procedure remote diventa quasi banale. Nonostante il nome, non è necessario conoscere o manipolare XML per usarli.

  • Il package email è una libreria per manipolare i messaggi email, che include MIME e altri documenti basati sulla RFC 2822. A differenza di smtplib e poplib, che ricevono e spediscono messaggi, questo package fornisce un set di strumenti completo per costruire e decodificare strutture complesse, allegati inclusi, e per implementare gli encoding di internet e i protocolli degli header.

  • Il package json supporta il parsing di questo popolare formato d’interscambio. Il modulo csv fornisce strumenti per la lettura e scrittura di file in formato CSV, molto diffuso per i database e i fogli di calcolo. La gestione di XML è garantita dai package xml.etree.ElementTree, xml.dom e xml.sax. Complessivamente, questi moduli e package semplificano molto lo scambio di informazioni tra le applicazioni Python e il mondo esterno.

  • Il modulo sqlite3 permette l’accesso ai database SQLite, mettendo a disposizione uno strumento di persistenza accessibile con una sintassi SQL leggermente modificata.

  • L’internazionalizzazione è garantita da un gran numero di moduli come gettext, locale e il package codecs.