Lectura de fitxers molt grans amb Generator (yield)

Els algorismes que hem vist fins ara carreguen tot el fitxer a la memòria. Això pot ser lent i ineficient quan són grans, i més en servidors d'aplicacions web.

Per això, podem utilitzar un Generator, que el que fa és anar llegint el fitxer línia per línia.

A continuació, per provar com funciona un Generador i comprovar que aquesta hipotesi és certa (que amb Genrator i yield estalviem temps i recursos), desenvoluparem un benchmark per comparar els dos enfocaments utilitzant un fitxer de 10,000 línies i 12 columnes.

import numpy as np
import time

# Funció per llegir tot el fitxer a la memòria
def read_file_all_at_once(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()
        processed_lines = [line.strip() for line in lines]
    return processed_lines

# Funció per llegir el fitxer línia per línia amb generador
def read_file_with_generator(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line.strip()

# Generar fitxer de prova
num_lines = 12000
num_columns = 50
data = np.random.rand(num_lines, num_columns)

file_path = 'large_file.txt'
with open(file_path, 'w') as file:
    for row in data:
        file.write(','.join(map(str, row)) + '\n')

# Benchmarking sense generadors
start_time = time.time()
all_lines = read_file_all_at_once(file_path)
end_time = time.time()
print("Temps de lectura sense generadors:", end_time - start_time)

# Benchmarking amb generadors
start_time = time.time()
for line in read_file_with_generator(file_path):
    pass  # Processa cada línia aquí
end_time = time.time()
print("Temps de lectura amb generadors:", end_time - start_time)

Resultats del Benchmarking:

Temps de lectura sense generadors: 0.05058765411376953

Temps de lectura amb generadors: 0.017226219177246094

Com veiem, el temps de lectura amb generadors és inferior, per tant és més eficient a nivell de temps.

Com es crea el Generador ?

La gràcia de la funció read_file_with_generator és que en comptes de tenir el return té la paraula clau yield.

La paraula clau yield és la clau que converteix aquesta funció en un generador. En lloc de retornar un valor i sortir de la funció (com faria return), yield retorna una línia processada (sense espais en blanc al principi i al final) i suspèn l'execució de la funció. La propera vegada que es cridi next() sobre l'iterador retornat per aquesta funció, la execució es reprèn just després del yield.


Referències consultades:

  1. https://www.udacity.com/blog/2021/09/getting-started-with-try-except-in-python.html
  2. https://realpython.com/python-download-file-from-url/
  3. https://www.udacity.com/blog/2021/09/getting-started-with-try-except-in-python.html

Referències pendents d'incloure per ampliar:

  1. https://medium.com/@technige/what-does-requests-offer-over-urllib3-in-2022-e6a38d9273d9
  2. https://stackabuse.com/bytes/how-to-unzip-a-gz-file-using-python/
  3. https://bito.ai/resources/unzip-gz-file-python-python-explained/ . https://pywombat.com/articles/shutil-python

Relatives a AsyncIO: