Encontrar funciones definidas en un con: Bloque
-
12-09-2019 - |
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
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 def
s 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)