2009-08-10 6 views
9

Voici un code de Richard Jones' Blog:Trouver définies dans un avec: Bloc

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 

Ma question est: comment diable at-il fait cela? Comment le gestionnaire de contexte peut-il accéder à la portée à l'intérieur du bloc with? Voici un modèle de base pour essayer de comprendre ceci:

Répondre

11

est ici une façon:

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

Modifier: Code étiré un peu plus, a ajouté quelques explications ...:

Attraper les habitants de l'appelant au __exit__ est facile - plus difficile est d'éviter les locaux qui étaient déjà définis avant le bloc with, ce qui explique pourquoi j'ai ajouté à deux principaux fonctions locales que le with doit ignorer. Je ne suis pas 100% satisfait de cette solution, qui semble un peu compliquée, mais je n'ai pas réussi à obtenir des tests d'égalité corrects avec == ou is, donc j'ai eu recours à cette approche plutôt compliquée.

J'ai également ajouté une boucle (pour être plus sûr que les def s avant/dans/après sont correctement gérés) et une vérification de type et un appel de fonction pour s'assurer que la bonne incarnation de testing est la cela est identifié (tout semble bien fonctionner) - bien sûr le code comme écrit ne fonctionne que si le def à l'intérieur du with est pour une fonction appelable sans arguments, il n'est pas difficile d'obtenir la signature avec inspect pour parer contre cela (mais depuis que je Je fais l'appel uniquement dans le but de vérifier que les bons objets de fonction sont identifiés, je ne me suis pas soucié de ce dernier raffinement ;-).

+0

Lovely, merci beaucoup. – llimllib

+1

De rien! était un problème amusant à aborder, donc tx pour le poser ;-). –

+1

J'ai posté une entrée de blog sur l'utilisation du code que vous m'avez donné, au cas où vous seriez intéressé: http://billmill.org/multi_line_lambdas.html – llimllib

1

Pour répondre à votre question, oui, c'est l'introspection du cadre.

Mais la syntaxe je créerais à faire la même chose est

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 

Ici, je mettre en œuvre gui.button comme un décorateur qui renvoie par exemple le bouton donné quelques paramètres et événements (bien qu'il me semble maintenant que button = gui.button('click me!', mybutton_onclick est bien aussi).

Je laisserais également gui.vertical car il peut être implémenté sans introspection. Je ne suis pas sûr de sa mise en œuvre, mais il peut impliquer la mise gui.direction = gui.VERTICAL de sorte que gui.label() et d'autres l'utilisent dans le calcul de leurs coordonnées.

Maintenant, quand je regarde, je pense que je vais essayer la syntaxe:

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'idée étant que de la même façon dont l'étiquette est faite de texte, un bouton est faite de texte et fonction)

+0

mais pourquoi utiliser "avec gui.vertical"? Il aurait besoin de faire la même introspection de pile pour avoir accès au texte, aux éléments et au bouton qui s'y trouvent.Je suis sûr que vous faites quelque chose comme: classe MyLayout (gui.Vertical): text = gui.label ('bonjour!) #etc droit? De toute façon, je suis bien conscient que c'est un abus gravement non standard du bloc with. Je voulais juste savoir comment il l'a fait. Je souhaite que vous voyiez au moins que c'est un mauvais abus du bloc avec :) – llimllib

+0

Je lis 'avec gui.vertical' comme" ne crée aucun élément, mais fais en sorte que tous les éléments créés dans ce contexte calculent leurs coordonnées verticalement à partir du courant point". Pas d'introspection. –

Questions connexes