Pregunta

Aquí hay un código de Richard Jones' Blog :

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 gestor de contexto acceder el alcance dentro del bloque? Aquí hay una plantilla básica para tratar de resolver esto:

from __future__ import with_statement

class button(object):
  def __enter__(self):
    #do some setup
    pass

  def __exit__(self, exc_type, exc_value, traceback):
    #XXX: how can we find the testing() function?
    pass

with button():
  def testing():
    pass
¿Fue útil?

Solución

Esto es 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 : código de estirado un poco más, añadió alguna explicación ...:

La captura de los locales de la persona que llama al __exit__ es fácil - más difícil es evitar esos locales que ya se definieron antes bloque with, por lo que he añadido a las principales funciones de dos locales que el with debe ignorar. No estoy 100% satisfecho con esta solución, que parece un poco complicado, pero no pude conseguir la igualdad de probar el correcto, ya sea con o == is, así que recurrí a este enfoque bastante complicado.

También he añadido un bucle (para hacer más fuertemente que los defs antes / en / después de que se están manejando correctamente) y un tipo de comprobación y la función de guardia para asegurarse de que la encarnación derecha de testing es el que está identificado (todo parece funcionar bien) - por supuesto el código como está escrito sólo funciona si el def dentro del with es exigible para una función sin argumentos, no es difícil conseguir la firma con inspect para protegerse contra la que (pero ya que soy haciendo la llamada sólo con el propósito de comprobar que se identifican los objetos de función derecha, no me molesté sobre este último refinamiento; -).

Otros consejos

Para responder a su pregunta, sí, es la introspección marco.

Sin embargo, la sintaxis crearía a hacer lo mismo 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

A continuación, 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 está muy bien también).

Me dejo también gui.vertical como lo es ya que puede llevarse a cabo sin la introspección. No estoy seguro acerca de su aplicación, pero puede implicar el establecimiento gui.direction = gui.VERTICAL modo que gui.label() y otros lo utilizan 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)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top