2012-06-22 12 views
6

estoy confundido acerca de este comportamiento alcance:extraña función de Python comportamiento alcance

class Bar: 
    def __init__(self): 
     for fn in ["open","openW","remove","mkdir","exists","isdir","listdir"]: 
      print "register", fn 
      def func_wrapper(filename): 
       print "called func wrapper", fn, filename 
      setattr(self, fn, func_wrapper) 

bar = Bar() 
bar.open("a") 
bar.remove("b") 
bar.listdir("c") 

Esto le da a la salida:

register open 
register openW 
register remove 
register mkdir 
register exists 
register isdir 
register listdir 
called func wrapper listdir a 
called func wrapper listdir b 
called func wrapper listdir c 

Pero lo que habría esperado que func_wrapper siempre habría la función correcta. Sé que el alcance de func_wrapper es para toda la función, pero la redefino en cada iteración de bucle y la última instancia se guardó en attrib. También traté de agregar func_wrapper = None debajo del setattr pero eso no ayuda (también me habría preguntado ...).

¿Estoy ciego? Ni siquiera veo cómo solucionarlo/arreglarlo.

+0

@heltonbiker: ¿Pueden dar más detalles? ¿Cómo debería usar un dict en su lugar aquí? ¿Y por qué? – Albert

+0

He leído más a fondo y he eliminado el comentario anterior. – heltonbiker

Respuesta

6

Ya sea con

class Bar: 
    def __init__(self): 
     for fn in ["open","openW","remove","mkdir","exists","isdir","listdir"]: 
      print "register", fn 
      def func_wrapper(filename, fn=fn): 
       print "called func wrapper", fn, filename 
      setattr(self, fn, func_wrapper) 

o, de forma más robusta, con

def mkwrapper(fn): 
    def func_wrapper(filename): 
     print "called func wrapper", fn, filename 
    func_wrapper.__name__ = fn 
    return func_wrapper 

class Bar: 
    def __init__(self): 
     for fn in ["open","openW","remove","mkdir","exists","isdir","listdir"]: 
      print "register", fn 
      func_wrapper = mkwrapper(fn) 
      setattr(self, fn, func_wrapper) 

En su ejemplo original, todas las funciones generadas accede a la misma variable exterior fn, que cambia en cada ejecución del bucle. En los ejemplos corregidos, esto se previene.

+0

Ah sí, pensé algo como esto ... :) – Albert

+0

Siempre pensé que había algo * especial * sobre 'lambda' con respecto a la unión tardía, pero ahora entiendo que las funciones regulares realmente tienen el mismo problema, es solo que no se utilizan tan a menudo de la misma manera que las funciones lambda. +1 a pregunta y respuesta. –

+0

Realmente me gustaría que la gente dejara de recomendar ese hack de parámetro predeterminado. –

Cuestiones relacionadas