Gruparea și Înlănțuirea Datelor cu itertools.batched și chain în Python

Modulul itertools Python 3.12+

Ghid complet pentru procesarea de fluxuri mari în Python: împărțirea pe loturi cu itertools.batched (Python 3.12+), aplatizarea iteratoarelor cu chain și iterație eficientă cu memorie O(1).

Sintaxă
import itertools
for lot in itertools.batched(date_masive, 500):
    trimite_in_batch(lot)

Modulul standard itertools este una dintre cele mai puternice unelte din Python pentru scrierea de cod performant care consumă cantități minime de memorie RAM. Generatoarele din itertools calculează elementele la cerere (lazy), permițând procesarea unor seturi de date de zeci de gigaocteți cu o amprentă de memorie constantă ($O(1)$). Începând cu Python 3.12, modulul include funcția mult așteptată itertools.batched().

1. Împărțirea pe loturi în Python 3.12+ cu itertools.batched
import itertools

# În producție, inserarea a 100.000 de înregistrări într-o bază de date
# sau trimiterea către un API extern trebuie făcută în loturi (batches) de câte 1000:
utilizatori_ids = range(1, 10005) # generator de 10.004 elemente

# itertools.batched generează tupluri de lungimea cerută:
for index_lot, lot in enumerate(itertools.batched(utilizatori_ids, 1000), 1):
    # 'lot' este un tuplu cu exact 1000 de elemente (iar ultimul lot va avea 4):
    print(f"Procesez Lotul #{index_lot} cu {len(lot)} elemente...")
    # db.bulk_insert(lot)

2. Înlănțuirea și Aplatizarea Fluxurilor cu itertools.chain

Dacă aveți mai multe liste, fișiere sau fluxuri de date pe care doriți să le parcurgeți secvențial, adunarea lor clasică lista_a + lista_b copiază toate elementele într-o listă nouă în RAM. itertools.chain leagă iteratoarele într-un singur stream fără a aloca memorie suplimentară:

Parcurgerea mai multor surse fără alocare de memorie
clienti_activi = ['Ana', 'Bogdan']
clienti_noi = ['Cristian', 'Diana']
clienti_arhiva = ('Elena', 'Florin')

# chain creează un iterator peste toate sursele:
for client in itertools.chain(clienti_activi, clienti_noi, clienti_arhiva):
    print(f"Notificare trimisă către: {client}")

# Aplatizarea unei liste de liste (flattening) cu chain.from_iterable:
matrice = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
numere_aplatizate = list(itertools.chain.from_iterable(matrice))
# Rezultat: [1, 2, 3, 4, 5, 6, 7, 8, 9]

3. Paginare Leneșă cu itertools.islice

Pentru a extrage o felie (slice) dintr-un generator infinit sau dintr-un fișier text masiv fără a citi tot conținutul în memorie:

with open("loguri_mari.txt") as f:
    # Sare peste primele 100 de linii și citește următoarele 20 (echivalent LIMIT 20 OFFSET 100):
    for linie in itertools.islice(f, 100, 120):
        print(linie.strip())