Pergunta

Aqui está um 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

A minha pergunta é: como diabos ele faz isso? Como pode o gerente contexto acesso do escopo dentro do com o bloco? Aqui está um modelo básico para tentar descobrir isso:

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
Foi útil?

Solução

Aqui está uma maneira:

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 esticado um pouco mais, acrescentou alguma explicação ...:

Catching moradores do chamador na __exit__ é fácil - mais complicado é evitar os habitantes locais que já foram definidos antes o bloco with, que é por isso que eu adicionado a duas principais funções locais que o with deve ignorar. Eu não estou 100% feliz com esta solução, que parece um pouco complicado, mas eu não poderia começar a igualdade testando correta com qualquer == ou is, então eu recorreu a esta abordagem bastante complicado.

Eu também acrescentou um loop (para fazer mais fortemente se que os defs antes / dentro / depois estão a ser devidamente tratado) e um tipo de verificação e função chamada para certificar-se a encarnação direito de testing é aquele que é identificado (tudo parece funcionar bem) - é claro que o código como está escrito só funciona se o def dentro do with é para um exigível função sem argumentos, não é difícil para obter a assinatura com inspect para enfermaria contra isso (mas desde que eu sou fazendo a chamada somente com a finalidade de verificar se os objetos de função direita são identificados, eu não me incomodei sobre este último refinamento; -).

Outras dicas

Para responder à sua pergunta, sim, é quadro introspecção.

Mas a sintaxe eu iria criar para fazer a mesma coisa é

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

Aqui eu iria implementar gui.button como um decorador que instância de botão retornos dado alguns parâmetros e eventos (embora parece-me agora que button = gui.button('click me!', mybutton_onclick é bom também).

Eu também deixaria gui.vertical como é, uma vez que pode ser implementado sem introspecção. Eu não tenho certeza sobre a sua execução, mas pode envolver a fixação gui.direction = gui.VERTICAL para que gui.label() e outros usá-lo no cálculo suas coordenadas.

Agora, quando eu olhar para isso, eu acho que ia tentar a sintaxe:

    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

(a idéia é que da mesma forma como etiqueta é feita fora do texto, um botão é feita fora do texto e função)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top