2010-04-09 14 views
13

tengo código como este:Python función anidada alcances

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) 

Pero ctr responde 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 

¿Cómo puedo solucionar este problema? Pensé que los telescopios nester me habrían permitido hacer esto. Lo he intentado con 'global', pero todavía no funciona.

+0

también miran a esta pregunta: http://stackoverflow.com/questions/2516652/scoping-problem-in-recursive-closure –

Respuesta

19

Dado que la función anidada no puede volver a enlazar un nombre no local (en Python 2; en Python 3, usaría la instrucción nonlocal para habilitar eso), debe realizar su incremento sin volver a enlazar el nombre (manteniendo el contador como un elemento o atributo de algún nombre simple, no como como un nombre simple). Por ejemplo:

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

y por supuesto usar ctr[0] donde quiera que estés usando desnuda ctr ahora en otro lugar.

+1

Esto parece casi un 'hack'. Lo usaré, pero parece una limitación de Python 2.x. Pero creo que usaré 3.x pronto. –

+0

Alex - gracias - este problema de alcance estaba desordenando la composición de las funciones en 2.x – Ben

5

De 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.

Por lo tanto, lo que se necesita para pasar a ctrkeyfunc explícitamente.

+0

He intentado hacer keyfunc def (x, k = CTR), y luego hacer k + = 1. Pero eso no puede funcionar, porque entonces k pasa a formar parte del alcance local de la función y no actualiza la variable externa, ctr. –

+0

La actualización de 'ctr' debe hacerse en su propio ámbito. Puede actualizarlo antes o después de las llamadas a 'keyfunc'. – danben

+0

Eso no sería posible ya que es un argumento para sorted() y es llamado por el interno de Python, no por mí directamente. No pude ponerlo en una lambda porque las lambdas solo permiten expresiones afaik. –

0

¿Qué tal declarar ctr fuera de la clase a la que pertenece write_postcodes, o cualquier otra clase/función? Esto hará que la variable sea accesible y escribible.

+1

Sí, pero es un poco complicado tener variables globales solo para una o dos funciones, es mejor guardarlas en ámbitos locales para que sepa por qué los puso allí en primer lugar. –

Cuestiones relacionadas