2013-06-10 18 views
5

Non mi riferisco alle chiusure, dove la funzione esterna restituisce la funzione interna, o alla memoizzazione, in particolare. Ci sono stati alcuni casi in cui ho voluto scrivere una funzione ricorsiva, magari con la memoizzazione, e sembrava molto più semplice inizializzare aa dict o qualche altra struttura di dati nella funzione esterna, e quindi avere una funzione di supporto ricorsiva che scriveva e accedere al dict e agli argomenti della funzione esterna. Ecco quello che voglio dire -Quando è opportuno utilizzare le funzioni annidate in Python?

def foo(arr, l): 
    cache = {} 
    result = [] 

    def recursive_foo_helper(i, j, k): 
     # some code that depends on arr, l, i, j, k, cache, and result 

    for (x, y) in arr: 
     if recursive_foo_helper(x, y, k): 
     return result 
    return None 

invece di avere la funzione di supporto dichiarato separatamente con qualche firma eccellente lunghi come,

recursive_foo_helper(arr, l, i, j, k, cache={}, result=[]) 

Ho letto che fare questo è abbastanza standard per Memoizzazione, ma ero curioso se c'è un consenso sul fatto che sia OK fare solo per le funzioni di aiuto ricorsive.

+0

Se non si chiama la funzione in altri punti, non riesco a capire perché questa sarebbe una cosa negativa. – OptimusCrime

risposta

4

utilizzare una funzione annidata per trovare le corrispondenze da una lista:

def get_exact(data, key, match): 
    def is_match(item): 
     if (key in item) and (item[key].lower() == match.lower()): 
      return item 
     return False 
    return [i for i in data if is_match(i)] 

Non ci sono altre chiamate nel progetto hanno la necessità di utilizzare is_match (voce), quindi perché dichiararla separatamente?

Tuttavia, dirò che, per il mio esempio, la dichiarazione di is_match() al di fuori di get_exact() viene eseguita intorno a ~0.04 seconds più velocemente in 10.000 iterazioni.

def is_match(item, key, match): 
    if (key in item) and (item[key].lower() == match.lower()): 
     return item 
    return False 

def get_exact(data, key, match): 
    return [i for i in data if is_match(i, key, match)] 
1

direi con una chiusura come lei suggerisce è più pulita, ma ha davvero bisogno la parola nonlocal da python3 se si vuole associare nuovamente i riferimenti. Per oggetti mutabili è ovviamente ok modificarli.

Nel frattempo, è comune vedere gli hack defaultarg/idiomi in python2

One (forse l'unico) svantaggio della funzione annidata è che è difficile da unittest.

per assicurarsi che la cache non può crescere senza limiti, è necessario anche un modo per rimuovere gli elementi (ad esempio, meno di recente utilizzato)

1

Ci sono un sacco di buoni motivi. Personalmente, uso spesso funzioni annidate per mantenere pulito lo spazio dei nomi. È particolarmente utile all'interno metodi dell'oggetto:

class Foo(object): 
    def bar(self): 
     def baz(val): 
      return val 
     return [ baz(i) for i in range(1,101) ] 

Se Dichiaro baz fuori di bar, Mi sia bisogno di fare un metodo di Foo, o esporlo all'intero pacchetto.

2

Io di solito uso chiusure, tuttavia l'altro modo che suggerisci (quello che a volte chiamo Wrapper) è anche piuttosto utile e nella mia esperienza personale funziona perfettamente. Non ho mai avuto qualcuno che mi dicesse di evitare quello stile. Se il tuo codice funziona ed è leggibile (cosa che penso sia), allora fallo!

Problemi correlati