Question

Voici un code 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

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

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
Était-ce utile?

La solution

Voici 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 : étiré le code un peu plus, a ajouté quelques explications ...:

Attraper les habitants de l'appelant à __exit__ est facile - plus délicat est d'éviter les sections locales qui ont déjà été définis avant le bloc with, ce qui est la raison pour laquelle j'ai ajouté aux deux principales fonctions locales que le with doit ignorer. Je ne suis pas 100% satisfait de cette solution, qui semble un peu compliqué, mais je ne pouvais pas obtenir l'égalité test correcte avec soit == ou is, donc je eu recours à cette approche plutôt compliquée.

J'ai aussi ajouté une boucle (pour faire plus fortement que les defs avant / à l'intérieur / après sont correctement traitées) et un type contrôle et la fonction d'appel pour vous assurer que la bonne incarnation de testing est celui qui est identifié (tout semble bien fonctionner) - bien sûr le code tel qu'il est écrit ne fonctionne que si le def intérieur du with est une fonction appelable sans arguments, il est pas difficile d'obtenir la signature avec inspect pour parer contre cette (mais depuis que je suis faisant appel uniquement aux fins de vérifier que les objets de fonction droite sont identifiés, je ne l'ai pas pris la peine de cette dernière raffinement, -.)

Autres conseils

Pour répondre à votre question, oui, il 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 laisse aussi gui.vertical comme il est, car il peut être mis en œuvre sans l'introspection. Je ne suis pas sûr de sa mise en œuvre, mais il peut impliquer la mise en gui.direction = gui.VERTICAL afin 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 de la fonction)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top