Generatore

È un oggetto più semplice dell’iteratore. È una funzione che quando ritorna al chiamante, mantiene uno stato interno. Non crea effettivamente una sequenza, ma ha la logica per generarla.

yield prende il posto del return.

# Generator function
def fibonacci():
	a = 0
	b = 1
	while True:
		yeald b
		a,b = b, a+b

Il generatore è composto da due componenti fondamentali:

  1. generator function
  2. generatore

La generator function è una funzione che accetta parametri e restituisce un generatore. In realtà non viene restituito nulla perchè per i generatori non viene quindi deallocata la memoria, e quindi è il corpo della funzione il generatore vero e proprio.

Per iterare su un generatore si utilizza la funzione next, proprio come per gli iteratori. Questo impedisce il passaggio di parametri al generatore, una volta istanziato.

La generator function si riconosce dalla presenza di almeno una istruzione yield, solitamente al posto di un return. Quando il programma incontra una istruzione yield, salva lo stato della funzione e ritorna un valore. Quando viene chiamato next(generator_function), riprende dallo stato precedente.

Anche il generatore restituisce StopIteration quando arriva alla fine.

Dichiarazione implicita

Si può utilizzare la stessa struttura della List comprehension per creare un generatore implicitamente: [<espressione> for <id> in <iterabile> if <condizione>]

Moreover

I generatori possono anche essere utilizzati in un ciclo for:

def fibonacci(n):
    '''Trasformazione della funzione in generatore. L'istruzione yield
       semplicemente sostituisce la stampa.
    '''
    a = 0
    b = 1
    yield a
    for _ in range(n-2):
        yield b
        a,b = b,a+b
    yield b
 
for i in fibonacci(6):
	print(i)

Si può creare un simil range basto su un generatore:

def rangegen(*params):
    '''Generatore che simula l'iterabile range'''
    
    #### Controllo dei parametri e "setting" valori iniziale, finale e passo ####
    assert len(params)>=1 and len(params)<=3,"Illegal number of parameters"
    for x in params:
        if type(x) is not int:
            raise TypeError(f"'{type(x)}' object cannot be interpreted as an integer")     
    if len(params)==1:
        start = 0
        step = 1
        end = params[0]
    else:
        start = params[0]
        end = params[1]
        step = 1 if len(params)==2 else params[2]
    ###############################################################################
    i = start
    while step>0 and i<end or step<0 and i>end:
        yield i 
        i += step

python_book5