Test delle unità per le pipeline di data munging costituite da funzioni a una riga

9

Leggendo il Introduzione pratica alla programmazione funzionale di Mary Rose Cook, dona come un esempio di anti-pattern

def format_bands(bands):
    for band in bands:
        band['country'] = 'Canada'
        band['name'] = band['name'].replace('.', '')
        band['name'] = band['name'].title()

poiché

  • la funzione fa più di una cosa
  • il nome non è descrittivo
  • ha effetti collaterali

Come soluzione proposta, suggerisce la pipeline di funzioni anonime

pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
                      call(lambda x: x.replace('.', ''), 'name'),
                      call(str.title, 'name')])

Tuttavia questo mi sembra avere lo svantaggio di essere ancora meno controllabile; almeno format_bands potrebbe avere un test unitario per verificare se fa quello che è destinato a, ma come testare la pipeline? Oppure l'idea che le funzioni anonime siano così auto-esplicative da non dover essere testate?

La mia applicazione del mondo reale per questo sta cercando di rendere il mio codice pandas più funzionale. Avrò spesso una sorta di pipeline all'interno di una funzione "munging" "

def munge_data(df)
     df['name'] = df['name'].str.lower()
     df = df.drop_duplicates()
     return df

Oppure riscrivi nello stile della pipeline:

def munge_data(df)
    munged = (df.assign(lambda x: x['name'].str.lower()
                .drop_duplicates())
    return munged

Qualche suggerimento per le migliori pratiche in questo tipo di situazione?

    
posta Max Flander 14.12.2015 - 23:35
fonte

1 risposta

1

Penso che tu abbia mancato probabilmente la parte più importante dell'esempio corretto del libro. La modifica più fondamentale del codice proviene dal metodo che utilizza tutti i valori di un elenco per operare su un elemento.

Esistono già funzioni come iter (in questo caso denominata pipeline_foreach ) che eseguono una determinata operazione su tutti gli elementi in una lista. Non è stato necessario duplicarlo con un ciclo for . Anche l'utilizzo di un elenco ben noto rende chiara la tua intenzione. Con map stai trasformando i valori. Con iter stai eseguendo un effetto collaterale con ogni elemento. Con for loop sei ... beh, non lo sai fino a quando non lo guardi.

Il codice corretto di esempio non è ancora molto funzionale, perché (per quanto posso dire) modifica i valori nell'elenco senza restituirli, impedendo ulteriori piping o composizione di funzioni. Il metodo funzionalmente preferito map creerebbe un nuovo elenco di bande con country aggiornato e name . Quindi puoi reindirizzare l'output alla funzione successiva o comporre map con un'altra funzione che ha preso un elenco di bande. Con iter , è come un vicolo cieco di pipeline.

Penso che il codice del risultato finale abbia delle piccole funzioni che sono troppo banali per disturbare i test qui. Dopotutto, non è necessario scrivere test unitari contro replace o title . Ora forse si desidera comporre questi insieme nella propria funzione e test unitario per ottenere la combinazione desiderata su un singolo oggetto. Probabilmente avrei probabilmente modificato format_bands in format_band singolare, abbandonato il ciclo for e chiamato pipeline_each(bands, format_band) . Quindi puoi testare format_band per assicurarti di non aver dimenticato qualcosa.

Comunque, sul tuo codice. Il tuo secondo esempio di codice sembra più pipeline-y. Ma questo da solo non offre i vantaggi della programmazione funzionale. In pratica, la programmazione funzionale significa garantire la compatibilità delle funzioni con altre funzioni definendone la compatibilità solo in termini di input e output. Se all'interno della funzione sono presenti effetti secondari nascosti, nonostante il suo input / output allineato con un'altra funzione, non è possibile sapere se sono compatibili fino al runtime. Tuttavia, se due funzioni sono prive di effetti collaterali e corrispondono a output-to-input, puoi eseguire pipeline o comporre li con poca preoccupazione per risultati inaspettati.

    
risposta data 04.06.2016 - 02:08
fonte

Leggi altre domande sui tag