2009-08-10 8 views
9

Oto niektóre kodu z Richard Jones' Blog:Znalezienie Funkcje definiowane w z: Bloku

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 

Moje pytanie brzmi: jak do cholery zrobił to zrobić? W jaki sposób menedżer kontekstów może uzyskać dostęp do zakresu wewnątrz bloku with? Oto podstawowy szablon dla próby rysunek to:

Odpowiedz

11

Oto jeden ze sposobów:

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

Edit: rozciągnięte kod nieco więcej, dodano kilka wyjaśnienia ...:

Łapanie lokatorów rozmówcy pod numerem __exit__ jest łatwe - trudniejsze jest uniknięcie tych loci, które zostały zdefiniowane jako przed bloku with, dlatego dodałem do dwóch głównych funkcje lokalne, które powinien zignorować with. Nie jestem w 100% zadowolony z tego rozwiązania, które wygląda nieco skomplikowanie, ale nie mogłem uzyskać poprawności testów poprawności z == lub is, więc uciekłem się do tego dość skomplikowanego podejścia.

Dodałem także pętlę (aby mieć większą pewność, że def s przed/po/po są poprawnie obsługiwane) oraz kontrolę typu i wywołanie funkcji, aby upewnić się, że właściwe wcielenie testing jest tym samym to jest zidentyfikowane (wszystko wydaje się działać dobrze) - oczywiście kod jak napisano działa tylko wtedy, gdy def wewnątrz with służy do wywoływania funkcji bez argumentów, nietrudno jest uzyskać podpis z inspect, aby temu zapobiec (ale ponieważ "Wykonuję połączenie tylko w celu sprawdzenia, czy właściwe obiekty funkcji są zidentyfikowane, nie zawracałem sobie głowy tym ostatnim udoskonaleniem ;-).

+0

Cudownie, dziękuję bardzo. – llimllib

+1

Nie ma za co! był zabawny problem do rozwiązania, więc tx za pozowanie ;-). –

+1

Umieściłem wpis na blogu o użyciu kodu, który mi dałeś, na wypadek gdybyś był zainteresowany: http://billmill.org/multi_line_lambdas.html – llimllib

1

Aby odpowiedzieć na twoje pytanie, tak, jest to introspekcja klatek.

Ale składnia chciałbym tworzyć zrobić to samo jest

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 

Tu chciałbym wdrożyć gui.button jako dekorator, która zwraca instancję przycisku podane niektóre parametry i zdarzenia (choć wydaje mi się, że teraz button = gui.button('click me!', mybutton_onclick jest również dobrze).

Zostawiłabym również gui.vertical, ponieważ może być zaimplementowana bez introspekcji. Nie jestem pewien co do jego implementacji, ale może to wymagać ustawienia gui.direction = gui.VERTICAL, aby gui.label() i inne używały go do obliczania swoich współrzędnych.

Teraz, kiedy patrzę na to, myślę, że będę próbować składnię:

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 

(pomysł jest, że podobnie, jak etykieta jest wykonana z tekstem, przycisk jest wykonany z tekstem i funkcja)

+0

ale dlaczego używać "z gui.vertical"? Musiałby wykonać tę samą introspekcję stosu, aby uzyskać dostęp do tekstu, elementów i przycisku w nim.Jestem pewien, że robisz coś takiego: klasa MyLayout (gui.Vertical): text = gui.label ('hello!') #etc prawda? W każdym razie doskonale zdaję sobie sprawę, że jest to bardzo nietypowe nadużycie bloku with. Chciałem tylko wiedzieć, jak to zrobił. Mam nadzieję, że przynajmniej zobaczysz, że to fajne nadużycie bloku z :) – llimllib

+0

Przeczytałem 'z gui.vertical' jako" nie twórz żadnych elementów, ale upewnij się, że wszystkie elementy utworzone w tym kontekście obliczają swoje współrzędne pionowo od bieżącego punkt". Bez introspekcji. –