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
>>> # Returnează directorul curent (în care ne găsim chiar acum):
>>> os.getcwd()
'C:\\Python313'
>>> # Schimbăm directorul curent:
>>> os.chdir('/serverul/jurnalul_accesărilor')
>>> # Rulăm comanda mkdir în interpretorul de comenzi (shell)
>>> # al sistemului:
>>> os.system('mkdir lucru_astăzi')
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)
<returnează o listă cu toate funcțiile din modul>
>>> help(os)
<returnează o pagină extensivă de manual, bazată pe docstring-ul modulului>

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('datele.db', 'arhiva.db')
'arhiva.db'
>>> shutil.move('/build/executabilele', 'directorul_instalării')
'directorul_instalării'

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')
['numere_prime.py', 'numere_aleatoare.py', 'cote.py']

10.3. Argumente în linia de comandă

Scripturile utilitare pe care le întrebuințăm în mod obișnuit necesită adesea procesarea argumentelor primite în linia de comandă. Aceste argumente sunt stocate, sub formă de listă, în atributul argv al modulului sys. De exemplu, să presupunem că avem un fișier demonstrativ.py, cu următorul conținut:

# Fișierul demonstrativ.py
import sys
print(sys.argv)

Iată ce se va obține în urma execuției comenzii python demonstrativ.py unu doi trei în linie de comandă:

['demonstrativ.py', 'unu', 'doi', 'trei']

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:

# Conținutul scriptului capturi_de_la_început.py
import argparse

parserul = argparse.ArgumentParser(
    prog='capturi_de_la_început',
    description='Afișează primele rânduri din fiecare fișier')
parserul.add_argument('nume_de_fișiere', nargs='+')
parserul.add_argument('-l', '--rânduri', type=int, default=10)
argumentele = parserul.parse_args()
print(argumentele)

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('Atenție, nu găsesc jurnalul, încep unul nou\n')
Atenție, nu găsesc jurnalul, încep unul nou

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'\bm[a-zăâ]*', 'cine, maică, mâna ori mâncarea pică la măcel')
['maică', 'mâna', 'mâncarea', 'măcel']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'hap pe pe halat')
'hap pe halat'

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:

>>> 'ceai pentru Mircea'.replace('Mircea', 'Cici')
'ceai pentru Cici'

10.6. Matematică

Modulul math ne facilitează accesul la funcțiile corespondente ale bibliotecii C dedicate calculului (matematicii) în virgulă mobilă:

>>> 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(['măr', 'pară', 'banană'])
'măr'
>>> random.sample(range(100), 10)   # eșantionare fără înlocuire
[30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
>>> random.random()    # float aleator din intervalul [0.0, 1.0)
0.17970987693706186
>>> random.randrange(6)    # întreg aleator ales din range(6)
4

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

>>> import statistics
>>> datele = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
>>> statistics.mean(datele)
1.6071428571428572
>>> statistics.median(datele)
1.25
>>> statistics.variance(datele)
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/Europe/Bucharest.txt') as răspunsul:
...     for rândul in răspunsul:
...         # Convertește octeți în șir de caractere:
...         rândul = rândul.decode()
...         if rândul.startswith('datetime'):
...             # Elimină rândul gol de la final:
...             print(rândul.rstrip())
...
datetime: 2022-01-01T01:36:47.689215+00:00

>>> import smtplib
>>> serverul_de_poștă = smtplib.SMTP('localhost')
>>> serverul_de_poștă.sendmail('profetul@de_exemplu.org',
...                            'iuliu_cezar@de_exemplu.org',
... """To: iuliu_cezar@de_exemplu.org
... From: profetul@de_exemplu.org
...
... Bagă de seamă sunt Idele lui Martie!
... """)
>>> serverul_de_poștă.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).

>>> # datele calendaristice se construiesc și se formatează ușor
>>> from datetime import date
>>> acum = date.today()
>>> acum
datetime.date(2025, 7, 25)
>>> # evităm impunerea unor "locale" în Windows:
>>> acum.strftime("%d-%m-%y. %d %b %Y pică %A în ziua %d a lui %B.")
'25-07-25. 25 Jul 2025 pică Friday în ziua 25 a lui July.'

>>> # datele calendaristice suportă aritmetica de calendar
>>> ziua_de_naștere = date(1964, 7, 31)
>>> vârsta = acum - ziua_de_naștere
>>> vârsta.days
22274

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
>>> șirul = b'arac brac capac colac copac crac drac fac lac mac tac'\
...         b' toc trac'
>>> len(șirul)
62
>>> șirul_comprimat = zlib.compress(șirul)
>>> len(șirul_comprimat)
44
>>> zlib.decompress(șirul_comprimat)
b'arac brac capac colac copac crac drac fac lac mac tac toc trac'
>>> zlib.crc32(șirul)
2525718583

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 media(valorile):
...     """Calculează media aritmetică a unei liste de numere.
...
...     >>> print(media([20, 30, 70]))
...     40.0
...     """
...     return sum(valorile) / len(valorile)
...
>>> import doctest
>>> doctest.testmod()   # validează automat testele incluse
TestResults(failed=0, attempted=1)

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 TesteazăFuncțiileStatistice(unittest.TestCase):

    def testează_media(self):
        self.assertEqual(media([20, 30, 70]), 40.0)
        self.assertEqual(round(media([1, 5, 7]), 1), 4.3)
        with self.assertRaises(ZeroDivisionError):
            media([])
        with self.assertRaises(TypeError):
            media(20, 30, 70)

unittest.main()  # La apelul în linia de comandă vor fi invocate
                 # toate testele

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.