# 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:\\Python314'
    >>> 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)
    <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.

- The `email` package is a library for managing email messages, including MIME and other RFC 5322-based message documents. Unlike `smtplib` and `poplib` which actually send and receive messages, the email package has a complete toolset for building or decoding complex message structures (including attachments) and for implementing internet encoding and header protocols.

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