Encontrar funções definidas em um com: Bloco
-
12-09-2019 - |
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
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 def
s 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)