2009-08-10 9 views
9

Aquí hay un código de Richard Jones' Blog:Encontrar definida en una con: Bloque

with gui.vertical: 
    text = gui.label('hello!') 
    items = gui.selection(['one', 'two', 'three']) 
    with gui.button('click me!'): 
     def on_click(): 
      text.value = items.value 
      text.foreground = red 

Mi pregunta es: ¿cómo demonios hizo esto? ¿Cómo puede el administrador de contexto acceder al alcance dentro del bloque con? Aquí hay una plantilla básica para tratar de resolver esto:

Respuesta

11

Aquí hay una manera:

from __future__ import with_statement 
import inspect 

class button(object): 
    def __enter__(self): 
    # keep track of all that's already defined BEFORE the `with` 
    f = inspect.currentframe(1) 
    self.mustignore = dict(f.f_locals) 

    def __exit__(self, exc_type, exc_value, traceback): 
    f = inspect.currentframe(1) 
    # see what's been bound anew in the body of the `with` 
    interesting = dict() 
    for n in f.f_locals: 
     newf = f.f_locals[n] 
     if n not in self.mustignore: 
     interesting[n] = newf 
     continue 
     anf = self.mustignore[n] 
     if id(newf) != id(anf): 
     interesting[n] = newf 
    if interesting: 
     print 'interesting new things: %s' % ', '.join(sorted(interesting)) 
     for n, v in interesting.items(): 
     if isinstance(v, type(lambda:None)): 
      print 'function %r' % n 
      print v() 
    else: 
     print 'nothing interesting' 

def main(): 
    for i in (1, 2): 
    def ignorebefore(): 
     pass 
    with button(): 
     def testing(i=i): 
     return i 
    def ignoreafter(): 
     pass 

main() 

Editar: estira código un poco más, añadió alguna explicación ...:

Es fácil capturar a los usuarios locales en __exit__ - más complicado es evitar los locales que ya estaban definidos antes de el bloque with, por lo que agregué a los dos principales funciones locales que el with debe ignorar. No estoy 100% satisfecho con esta solución, que parece un poco complicada, pero no pude obtener la prueba de igualdad correcta con == o is, así que recurrí a este enfoque bastante complicado.

También he agregado un bucle (para asegurar más que el def s antes/dentro/después de que se maneje correctamente) y una comprobación de tipo y llamada de función para asegurarse de que la encarnación correcta de testing es la correcta eso es identificado (todo parece funcionar bien) - por supuesto, el código tal como está escrito solo funciona si el def dentro del with es para una función invocable sin argumentos, no es difícil obtener la firma con inspect para protegerse de eso (pero ya que Estoy haciendo la llamada solo con el propósito de verificar que los objetos funcionales correctos estén identificados, no me preocupé por este último refinamiento ;-).

+0

Encantador, muchas gracias. – llimllib

+1

¡De nada! fue un problema divertido de abordar, así que tx por posarlo ;-). –

+1

Publiqué una entrada de blog sobre el uso del código que me diste, en caso de que te interese: http://billmill.org/multi_line_lambdas.html – llimllib

1

Para responder a su pregunta, sí, es una introspección de cuadros.

Pero la sintaxis crearía a hacer la misma cosa es

with gui.vertical: 
    text = gui.label('hello!') 
    items = gui.selection(['one', 'two', 'three']) 
    @gui.button('click me!') 
    class button: 
     def on_click(): 
      text.value = items.value 
      text.foreground = red 

Aquí me gustaría poner en práctica gui.button como decorador que devuelve botón caso dado algunos parámetros y eventos (aunque me parece ahora que button = gui.button('click me!', mybutton_onclick es bien también).

También dejaría gui.vertical ya que se puede implementar sin introspección. No estoy seguro acerca de su implementación, pero puede implicar establecer gui.direction = gui.VERTICAL para que gui.label() y otros lo usen en el cálculo de sus coordenadas.

Ahora cuando miro a esto, creo que me gustaría probar la sintaxis:

with gui.vertical: 
     text = gui.label('hello!') 
     items = gui.selection(['one', 'two', 'three']) 

     @gui.button('click me!') 
     def button(): 
      text.value = items.value 
      foreground = red 

(la idea de que de manera similar a la forma en la etiqueta está hecho de texto, un botón está hecho de texto y función)

+0

, pero ¿por qué usar "con gui.vertical"? Tendría que hacer la misma introspección de pila para acceder al texto, a los elementos y al botón que contiene.Estoy seguro de que hacer algo como: MyLayout clase (gui.Vertical): texto = gui.label ('¡Hola!') #etc ¿verdad? De todos modos, soy consciente de que este es un abuso gravemente no estándar del bloque con. Solo quería saber cómo lo hizo. Espero que al menos vean que es un buen abuso del bloque :) :) – llimllib

+0

Leo 'con gui.vertical' como" no creo elementos, pero asegúrese de que todos los elementos creados en este contexto calculen sus coordenadas verticalmente desde el actual punto". Sin introspección. –

Cuestiones relacionadas