10. Turul pe scurt al bibliotecii standard

10.1. Interfața cu sistemul de operare

Modulul os ne furnizează o cantitate consistentă de funcții pentru interacțiunea cu sistemul de operare:

>>> import os
>>> os.getcwd()      # Return the current working directory
'C:\\Python311'
>>> os.chdir('/server/accesslogs')   # Change current working directory
>>> os.system('mkdir today')   # Run the command mkdir in the system shell
0

Aveți grijă să realizați importul în stilul dat de import os și nu scriind from os import *. Astfel, veți evita ca apelarea funcției os.open() să se producă în locul celei a funcției predefinite open(), aceasta din urmă funcționând complet diferit.

Funcțiile predefinite dir() și help() ne vor fi utile ca ajutoare interactive atunci când lucrăm cu module masive, precum os:

>>> import os
>>> dir(os)
<returns a list of all module functions>
>>> help(os)
<returns an extensive manual page created from the module's docstrings>

Pentru sarcinile administrative zilnice privind fișierele și directoarele, modulul shutil ne pune la dispoziție o interfață de nivel înalt care este (mai) ușor de întrebuințat:

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

10.2. Caractere de înlocuire pentru fișiere

Modulul glob dispune de o funcție care realizează liste de fișiere bazate pe căutări cu caractere de înlocuire (sau meta-caractere; de la englezescul wildcard) în directorul curent (și nu numai):

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

10.3. Argumente în linia de comandă

Common utility scripts often need to process command line arguments. These arguments are stored in the sys module’s argv attribute as a list. For instance the following output results from running python demo.py one two three at the command line:

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

Modulul argparse ne oferă un mecanism (mai) sofisticat pentru manipularea argumentelor primite în linia de comandă. Scriptul de mai jos extrage (din șirul de caractere al liniei de comandă) unul sau mai multe nume de fișiere, respectiv numărul opțional de rânduri ce urmează a fi afișate din fiecare din fișierele cu numele extrase:

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)

Atunci când rulăm în linie de comandă python capturi_de_la_început.py --rânduri=5 alfa.txt beta.txt, scriptul îi va atribui lui argumentele.rânduri valoarea 5 iar lui argumentele.nume_de_fișiere valoarea ['alfa.txt', 'beta.txt'].

10.4. Redirecționarea ieșirii erorilor și încheierea programelor

Modulul sys deține atribute și pentru stdin, stdout, respectiv pentru stderr. Acesta din urmă este de folos la emiterea avertismentelor și a mesajelor de eroare, chiar și atunci când (fluxul) stdout a fost redirecționat:

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

Modul (cel mai) direct de a încheia (de la englezescul terminate) execuția unui script este să utilizăm sys.exit().

10.5. Identificarea după tipare a șirurilor de caractere

Modulul re ne pune la dispoziție unelte care utilizează expresii regulate pentru procesări complexe ale șirurilor de caractere. Atunci când suntem interesați de identificări (de la englezescul matching) și manipulări sofisticate, expresiile regulate ne vin în ajutor cu soluții succinte, optimizate:

>>> 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'

Atunci când avem sarcini de identificare simple, metodele tipului șir de caractere sunt de preferat, dat fiind că ele pot fi citite (în codul Python aferent) și depanate mai ușor:

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

10.6. Matematică

The math module gives access to the underlying C library functions for floating point math:

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

Modulul random ne furnizează unelte pentru realizarea de eșantioane aleatoare:

>>> import random
>>> random.choice(['apple', 'pear', 'banana'])
'apple'
>>> random.sample(range(100), 10)   # sampling without replacement
[30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
>>> random.random()    # random float
0.17970987693706186
>>> random.randrange(6)    # random integer chosen from range(6)
4

Modulul statistics calculează proprietăți statistice de bază (media, mediana, varianța șamd.) pentru datele numerice:

>>> 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

Proiectul SciPy <https://scipy.org> deține multe alte module dedicate calculului numeric.

10.7. Accesul la Internet

Un număr de module sunt dedicate accesării Internetului și procesării protocoalelor acestuia. Două din cele mai simplu de folosit module sunt urllib.request, dedicat extragerii datelor din URL-uri, și smtplib, folosit la trimiterea de scrisori electronice. (Scrisoarea din exemplu conține versul shakespearian Beware the Ides of March. Ca traducere a sa am folosit versiunea dată de Barbu Lăzureanu):

>>> from urllib.request import urlopen
>>> with urlopen('http://worldtimeapi.org/api/timezone/etc/UTC.txt') as response:
...     for line in response:
...         line = line.decode()             # Convert bytes to a str
...         if line.startswith('datetime'):
...             print(line.rstrip())         # Remove trailing newline
...
datetime: 2022-01-01T01:36:47.689215+00:00

>>> 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()

(Atenție, la cel de-al doilea exemplu veți avea nevoie de un server de poștă electronică instalat pe localhost.)

10.8. Date calendaristice și momente de timp

Modulul datetime ne furnizează clase pentru manevrarea datelor calendaristice și a momentelor de timp atât în moduri simple cât și în feluri sofisticate. Deși calculul aritmetic cu date calendaristice și momente de timp este suportat, implementarea se concentrează pe extracția eficientă a componentelor (datelor temporale) în scopuri de formatare și de manipulare a afișărilor. Modulul suportă inclusiv obiecte care înțeleg fusurile orare (de la englezescul, ca jargon informatic, 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.'

>>> # dates support calendar arithmetic
>>> birthday = date(1964, 7, 31)
>>> age = now - birthday
>>> age.days
14368

10.9. Compresia datelor

Formatele tipice de compresie și de arhivare a datelor sunt suportate în mod direct de către module precum: zlib, gzip, bz2, lzma, zipfile și tarfile.

>>> 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. Măsurarea performanțelor

Nu de puține ori, programatorii se interesează de compararea performanțelor pe care le dovedesc diferitele abordări (cunoscute) ale aceleași probleme (de programare). Python-ul ne furnizează o unealtă de măsurare (a timpului de execuție) care poate satisface rapid aceste curiozități.

De exemplu, putem fi tentați, la un moment dat, să întrebuințăm împachetarea și despachetarea tuplurilor în locul abordării tradiționale la interschimbarea (de la englezescul swapping) unor valori. Modulul timeit ne dovedește imediat că folosirea tuplurilor ne va conferi un mic avantaj:

>>> 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

Spre deosebire de nivelul de granularitate (ridicat) al lui timeit, care îl face potrivit pe acesta pentru analiza unor fragmente mici de cod, modulele profile și pstats posedă unelte pentru identificarea zonelor critice în privința timpului de execuție din blocuri (mai) mari de cod Python.

10.11. Controlul calității

Una din căile care ne conduc la producerea software-ului de înaltă calitate este cea de a compune teste pentru verificarea fiecărei funcții (funcționalități) pe parcursul implementării ei și de a rula aceste teste în mod frecvent de-a lungul întregului proces de dezvoltare a produsului software.

Modulul doctest ne pune la dispoziție o unealtă care realizează analiza lexicală a codului (sau scanarea; de la englezescul scanning) unui modul și încearcă să valideze testele care au fost inserate (de către autorii codului) în docstring-urile acestui modul (program) Python. Prepararea unui asemenea test se rezumă la decuparea-și-lipirea (de la englezescul cut-and-paste) în docstring a fragmentului de cod format din apelul tipic al funcției (pe care vrem să o verificăm) împreună cu rezultatul (corect) returnat de acest apel. Astfel, pe de o parte, se îmbunătățește documentația oferindu-i-se utilizatorului unul sau mai multe exemple și, pe de altă parte, existența respectivelor fragmente îi va permite modulului doctest să verifice dacă programul pe care utilizatorul îl dezvoltă îi rămâne fidel documentației (inițiale):

def average(values):
    """Computes the arithmetic mean of a list of numbers.

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

import doctest
doctest.testmod()   # automatically validate the embedded tests

Modulul unittest nu este tot atât de fără efort la utilizare precum doctest, însă ne permite să construim un set de teste (mai) comprehensiv pe care să îl putem întreține într-un fișier separat:

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()  # Calling from the command line invokes all tests

10.12. Bateriile sunt incluse

Python-ul urmează filozofia „includeți (și) bateriile”. Putem observa cu ușurință aceasta dacă aruncăm o privire asupra capabilităților robuste și sofisticate pe care le probează pachetele sale cele mai cuprinzătoare. De pildă:

  • Modulele xmlrpc.client și xmlrpc.server transformă orice implementare de apeluri de procedură de la distanță într-o activitate aproape banală. În ciuda numelor acestor module, nu este nevoie nici de cunoștințe teoretice și nici de experință practică privind XML-ul.

  • Pachetul email este o bibliotecă dedicată administrării mesajelor de poștă electronică, inclusiv a mesageriei documentelor MIME și a altor documente bazate pe RFC 2822. Spre deosebire de smtplib și de poplib, care trimit și primesc efectiv mesajele, pachetul email deține un set complet de instrumente pentru construcția și decodificarea structurilor complexe de mesaje (incluzând aici și anexele, de la englezescul attachment, acestor mesaje), respectiv pentru implementarea protocoalelor de Internet privind codificările (textului) și antetele (mesajelor).

  • Pachetul json oferă suport robust pentru analiza sintactică și semantică a codului (sau parsarea; de la englezescul parsing) acestui format popular de interschimbare de date. Modulul csv ne permite citirea și scrierea directă a fișierelor în formatul valori-separate-prin-virgule (sau valori-delimitate-de-virgule; de la englezescul comma-separated-value), format acceptat de majoritatea bazelor de date și a foilor de calcul (de la englezescul spreadsheet). Procesările XML sunt suportate de pachetele xml.etree.ElementTree, xml.dom și xml.sax. Laolaltă, aceste module și pachete ușurează considerabil schimburile de date dintre aplicațiile Python și alte unelte informatice.

  • Modulul sqlite3 este o împachetare a bibliotecii de baze de date SQLite, care ne pune la dispoziție o bază de date persistentă (pe disc) ce va putea fi actualizată și accesată folosind o sintaxă SQL puțin atipică.

  • Internaționalizarea este facilitată de un număr de module, incluzându-le aici pe gettext, locale, precum și de pachetul codecs.