2010-04-09 11 views
13

Ho codice in questo modo:Python funzione nidificata Scopes

def write_postcodes(self): 
    """Write postcodes database. Write data to file pointer. Data 
    is ordered. Initially index pages are written, grouping postcodes by 
    the first three characters, allowing for faster searching.""" 
    status("POSTCODE", "Preparing to sort...", 0, 1) 
    # This function returns the key of x whilst updating the displayed 
    # status of the sort. 
    ctr = 0 
    def keyfunc(x): 
     ctr += 1 
     status("POSTCODE", "Sorting postcodes", ctr, len(self.postcodes)) 
     return x 
    sort_res = self.postcodes[:] 
    sort_res.sort(key=keyfunc) 

Ma ctr risponde con un NameError:

Traceback (most recent call last): 
    File "PostcodeWriter.py", line 53, in <module> 
    w.write_postcodes() 
    File "PostcodeWriter.py", line 47, in write_postcodes 
    sort_res.sort(key=keyfunc) 
    File "PostcodeWriter.py", line 43, in keyfunc 
    ctr += 1 
UnboundLocalError: local variable 'ctr' referenced before assignment 

Come posso risolvere questo problema? Pensavo che gli ambiti dei nester mi avrebbero permesso di farlo. Ho provato con "globale", ma non funziona ancora.

+0

anche a questa domanda: http://stackoverflow.com/questions/2516652/scope-problem-in-recursive-closure –

risposta

19

Poiché la funzione nidificata non può riassociare un nome non locale (in Python 2, in Python 3, si utilizzerà l'istruzione nonlocal per abilitarlo), è necessario eseguire l'incremento senza rifare il nome del bar (mantenendo il contatore come articolo o attributo di alcuni nomi di bar, non come barname stesso). Per esempio:

... 
ctr = [0] 
def keyfunc(x): 
    ctr[0] += 1 
    status("POSTCODE", "Sorting postcodes", ctr, len(self.postcodes)) 
    return x 
... 

e, naturalmente, utilizzare ctr[0] ovunque si sta utilizzando nudo ctr ora altrove.

+1

Sembra quasi un 'hack'. Lo userò, ma sembra una limitazione di Python 2.x. Suppongo che userò 3.x presto però. –

+0

Alex - grazie - questo problema di definizione degli obiettivi stava rendendo disordinata la composizione delle funzioni in 2.x – Ben

5

Da http://www.devshed.com/c/a/Python/Nested-Functions-in-Python/1/

Code in a nested function's body may access (but not rebind) local variables of an outer function, also known as free variables of the nested function.

Quindi, si avrebbe bisogno di passare ctr-keyfunc in modo esplicito.

+0

Ho provato a fare def keyfunc (x, k = ctr), e quindi facendo k + = 1. Ma questo non può funzionare, perché allora k diventa parte dell'ambito locale della funzione e non aggiorna la variabile esterna, ctr. –

+0

L'aggiornamento di 'ctr' deve essere eseguito nel proprio ambito. Potresti aggiornarlo prima o dopo le chiamate a 'keyfunc'. – danben

+0

Questo non sarebbe possibile poiché è un argomento di sorted() ed è chiamato dagli interni di Python, non da me direttamente. Non potrei metterlo in un lambda perché i lambda consentono solo espressioni afaik. –

0

Che ne dici di dichiarare ctr al di fuori della classe a cui appartengono i write_postcodes o qualsiasi altra classe/funzione? Ciò renderà la variabile accessibile e scrivibile.

+1

Sì, ma è un po 'caotico avere variabili globali solo per una o due funzioni - meglio tenerle in ambiti locali in modo da sapere perché li metti lì in primo luogo. –