Trovare funzioni definite in un con: Block
-
12-09-2019 - |
Domanda
Ecco un po 'di codice da 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
La mia domanda è: come diavolo ha fatto questo? Come si può accedere al direttore contesto l'ambito all'interno del blocco con? Ecco un modello di base per cercare di capire questo:
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
Soluzione
Ecco un modo:
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()
Modifica : allungato codice un po 'di più, ha aggiunto qualche spiegazione ...:
Facendo i locali del chiamante a __exit__
è facile - più difficile è di evitare quei locali che sono stati già definiti prima il blocco with
, che è il motivo per cui ho aggiunto ai due principali funzioni locali che il with
dovrebbe ignorare. Io non sono soddisfatto al 100% di questa soluzione, che sembra un po 'complicato, ma non ho potuto ottenere l'uguaglianza testare il corretto sia con ==
o is
, così ho fatto ricorso a questo approccio piuttosto complicata.
Ho anche aggiunto un loop (per rendere più forte che i def
s prima / entro / dopo vengono adeguatamente trattati) e di un tipo di controllo e la funzione di chiamata per assicurarsi il diritto incarnazione testing
è quello che è identificato (tutto sembra funzionare bene) - naturalmente il codice come scritto funziona solo se il def
all'interno del with
è per una funzione richiamabile senza argomenti, non è difficile per ottenere la firma con inspect
per scongiurare contro quella (ma visto che sono facendo la chiamata solo per lo scopo di verificare che gli oggetti funzione di destra sono identificati, non mi sono preoccupato di questo ultimo perfezionamento; -).
Altri suggerimenti
Per rispondere alla tua domanda, sì, è cornice introspezione.
Ma la sintassi vorrei creare a fare la stessa cosa è
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 questo punto vorrei implementare gui.button
come decoratore che restituisce un'istanza di pulsante dato alcuni parametri ed eventi (anche se mi sembra ora che button = gui.button('click me!', mybutton_onclick
va bene così).
Vorrei anche lasciare gui.vertical
come lo è dal momento che può essere attuato senza l'introspezione. Io non sono sicuro circa la sua attuazione, ma si può comprendere la definizione gui.direction = gui.VERTICAL
in modo che gui.label()
e altri lo utilizzano nel calcolo le loro coordinate.
Ora, quando guardo questo, penso che mi piacerebbe provare la sintassi:
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
(l'idea è che in modo simile a come etichetta è fatto di testo, un pulsante è fatto di testo e funzione)