2009-08-10 10 views
9

Hier einige Code von Richard Jones' Blog:Finding-Funktionen, die in einem mit: Block

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 

Meine Frage ist: wie zum Teufel er das tat? Wie kann der Kontextmanager auf den Bereich innerhalb des with-Blocks zugreifen? Hier ist eine grundlegende Vorlage für den Versuch, dies herauszufinden:

Antwort

11

Hier ist eine Möglichkeit:

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() 

bearbeiten: stretched Code ein bisschen mehr, eine Erklärung hinzugefügt ...:

Catching Einheimischen bei __exit__ fangen ist einfach - trickier ist das Vermeiden der Einheimischen, die bereits vor der with Block definiert wurden, weshalb ich Main zwei hinzugefügt lokale Funktionen, die der with ignorieren sollte. Ich bin nicht 100% zufrieden mit dieser Lösung, die ein wenig kompliziert aussieht, aber ich konnte weder mit == noch mit is die Gleichheitsprüfung korrigieren, also griff ich auf diesen ziemlich komplizierten Ansatz zurück.

Ich habe auch eine Schleife hinzugefügt (um sicherzustellen, dass die def s vor/in/nach werden ordnungsgemäß behandelt werden) und eine Art-Check-und Funktionsaufruf, um sicherzustellen, dass die richtige Inkarnation von testing ist das ist identifiziert (alles scheint gut zu funktionieren) - natürlich funktioniert der Code wie geschrieben nur, wenn die def innerhalb der with für eine Funktion ohne Argumente aufrufbar ist, ist es nicht schwer, die Signatur mit inspect dagegen abzuwehren (aber seit ich Ich mache den Anruf nur, um zu überprüfen, ob die richtigen Funktionsobjekte identifiziert sind, ich habe mich nicht um diese letzte Verfeinerung gekümmert ;-).

+0

Schön, vielen Dank. – llimllib

+1

Gern geschehen! war ein lustiges Problem zu bewältigen, also tx für das Posieren ;-). –

+1

Ich habe einen Blogeintrag über die Verwendung des Codes, den Sie mir gegeben haben, veröffentlicht, falls Sie interessiert sind: http://billmill.org/multi_line_lambdas.html – llimllib

1

Um Ihre Frage zu beantworten, ja, es ist Frame Introspektion.

Aber die Syntax ich schaffen würde, das Gleiche zu tun ist

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 

Hier würde ich gui.button als Dekorateur implementieren, die Schaltfläche Beispiel einige Parameter und Ereignisse gegeben zurück (obwohl es mir jetzt scheint, dass button = gui.button('click me!', mybutton_onclick ist auch gut).

Ich würde auch verlassen gui.vertical wie es ist, da es ohne Introspektion implementiert werden kann. Ich bin mir nicht sicher über die Implementierung, aber es kann beinhalten gui.direction = gui.VERTICAL, so dass gui.label() und andere verwenden es bei der Berechnung ihrer Koordinaten.

Nun, wenn ich dies zu betrachten, ich glaube ich die Syntax versuchen würde:

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 

(die Idee ist, dass ähnlich wie Etikett aus Text besteht, eine Schaltfläche aus Text besteht und Funktion)

+0

aber warum dann "mit gui.vertical" verwenden? Es müsste die gleiche Stapelprüfung durchführen, um Zugriff auf den darin enthaltenen Text, die Elemente und die Schaltfläche zu erhalten.Ich bin sicher, dass Sie so etwas wie: Klasse MyLayout (gui.Vertical): text = gui.label ('Hallo!') #etc richtig? Wie auch immer, ich bin mir bewusst, dass dies eine ernsthafte Nicht-Standard-Missbrauch der mit Block ist. Ich wollte nur wissen, wie er es gemacht hat. Ich hoffe, Sie zumindest sehen, dass es ein cooler Missbrauch des mit Block ist :) – llimllib

+0

I 'mit gui.vertical' als„erstellen keine Elemente lesen, aber stellen Sie sicher, dass alle in diesem Zusammenhang erstellten Elemente ihre Koordinaten vertikal von der aktuellen Berechnung Punkt". Keine Introspektion. –

Verwandte Themen