2012-09-26 11 views
10

Definiuję klasy menedżera kontekstu i chciałbym móc pominąć blok kodu bez zgłaszania wyjątku, jeśli pewne warunki są spełnione podczas wystąpienia. Na przykład:Pomijanie wykonywania-z-bloku

class My_Context(object): 
    def __init__(self,mode=0): 
     """ 
     if mode = 0, proceed as normal 
     if mode = 1, do not execute block 
     """ 
     self.mode=mode 
    def __enter__(self): 
     if self.mode==1: 
      print 'Exiting...' 
      CODE TO EXIT PREMATURELY 
    def __exit__(self, type, value, traceback): 
     print 'Exiting...' 

with My_Context(mode=1): 
    print 'Executing block of codes...' 
+0

Znalazłem to, ale nie wiem, jak to zrozumieć, ani jak go wdrożyć. http://www.python.org/dev/peps/pep-0377/ Czy istnieją inne, bardziej eleganckie sposoby? –

+0

Fakt, że jest to PEP (i dyskusja nad zmianami semantycznymi) sugeruje, że nie można go wdrożyć bez uciekania się do zmiany zachowania tłumacza. – nneonneo

+2

ma obsesję na punkcie schludności? :) z A(), B(): gdzie B __enter__ może podnieść coś wydaje mi się w porządku. – swang

Odpowiedz

8

Jeśli chcesz rozwiązanie ad hoc, który wykorzystuje pomysły z withhacks (w szczególności z AnonymousBlocksInPython), to będzie działać:

import sys 
import inspect 

class My_Context(object): 
    def __init__(self,mode=0): 
     """ 
     if mode = 0, proceed as normal 
     if mode = 1, do not execute block 
     """ 
     self.mode=mode 
    def __enter__(self): 
     if self.mode==1: 
      print 'Met block-skipping criterion ...' 
      # Do some magic 
      sys.settrace(lambda *args, **keys: None) 
      frame = inspect.currentframe(1) 
      frame.f_trace = self.trace 
    def trace(self, frame, event, arg): 
     raise 
    def __exit__(self, type, value, traceback): 
     print 'Exiting context ...' 
     return True 

Porównaj następujące:

with My_Context(mode=1): 
    print 'Executing block of code ...' 

z

with My_Context(mode=0): 
    print 'Executing block of code ... ' 
+0

Tego właśnie szukałem. tytytytytyty. –

+1

Rozumiem, więc uruchamia TypeError w jakiś sposób, który jest przechwytywany i blokowany przez metodę __exit __(). Interesująca praca wokół! –

+0

Dodałem pętlę if w metodzie __exit __(), aby sprawdzić typ i wartość, tak aby tylko wyjątek podniesiony przez hack został wyłączony. –

1

To, co próbujesz zrobić, niestety nie jest możliwe. Jeśli __enter__ zgłasza wyjątek, ten wyjątek jest zgłaszany na oświadczeniu with (__exit__ nie jest wywoływany). Jeśli nie podniesie wyjątku, to zwracana wartość jest podawana do bloku i blok jest wykonywany.

najbliższą rzeczą mogę myśleć to wyraźnie zaznaczone flagi w bloku:

class Break(Exception): 
    pass 

class MyContext(object): 
    def __init__(self,mode=0): 
     """ 
     if mode = 0, proceed as normal 
     if mode = 1, do not execute block 
     """ 
     self.mode=mode 
    def __enter__(self): 
     if self.mode==1: 
      print 'Exiting...' 
     return self.mode 
    def __exit__(self, type, value, traceback): 
     if type is None: 
      print 'Normal exit...' 
      return # no exception 
     if issubclass(type, Break): 
      return True # suppress exception 
     print 'Exception exit...' 

with MyContext(mode=1) as skip: 
    if skip: raise Break() 
    print 'Executing block of codes...' 

ta pozwala także podnieść Break() w środku with bloku symulacji normalnego break oświadczenie.

+0

Flaga działa, ale chciałbym zachować wszystkie kontrole w menedżerze kontekstu i utrzymywać blok kodów w czystości. Jeśli to niemożliwe, być może będę musiał znaleźć inny sposób poza tym. Dziękuję bardzo! –

10

Według PEP-343, A with oświadczenie przekłada od:

with EXPR as VAR: 
    BLOCK 

do:

mgr = (EXPR) 
exit = type(mgr).__exit__ # Not calling it yet 
value = type(mgr).__enter__(mgr) 
exc = True 
try: 
    try: 
     VAR = value # Only if "as VAR" is present 
     BLOCK 
    except: 
     # The exceptional case is handled here 
     exc = False 
     if not exit(mgr, *sys.exc_info()): 
      raise 
     # The exception is swallowed if exit() returns true 
finally: 
    # The normal and non-local-goto cases are handled here 
    if exc: 
     exit(mgr, None, None, None) 

Jak widać, nie jest niczym oczywistym można zrobić na podstawie zaproszenia do sposobu __enter__() menedżer kontekstu, który może pominąć treść ("BLOCK") instrukcji with.

Ludzie wykonali specyficzne dla implementacji Pythona rzeczy, takie jak manipulowanie stosem wywołań wewnątrz __enter__(), w projektach takich jak withhacks. Pamiętam, jak Alex Martelli opublikował bardzo interesujący wywiad z hackem na stackoverflow za rok lub dwa z powrotem (nie pamiętam wystarczająco dużo postu z ręki, aby go znaleźć i znaleźć).

Ale prostą odpowiedzią na twoje pytanie/problem jest to, że nie możesz zrobić tego, o co prosisz, pomijając treść instrukcji, bez uciekania się do tak zwanej "głębokiej magii" (która niekoniecznie jest przenośna między pythonami wdrożenia). Z głęboką magią możesz być w stanie to zrobić, ale zalecam robić tylko takie rzeczy, jak ćwiczenie, aby zobaczyć, jak można to zrobić, nigdy w "kodzie produkcyjnym".

+1

O mój boże 'withhacks' jest szalony, zrobili bloki w stylu Ruby w Pythonie ... – nneonneo

+0

OK, to dużo wyjaśnia. Sprawdziłem withhacks. Wydaje mi się, że w tej chwili jest poza mną ... Nie jestem pewien, jak mogę użyć tego kodu do wykonania przeskoku, ale są z pewnością interesujące fragmenty kodów, których mógłbym użyć. (Aktualizacja: BLOKOWANIE STYLU RUBY?) Widzę, hahahah ... To naprawdę obłąkańcze) W przeciwnym razie naprawdę będę musiał pomyśleć o innym podejściu. Dziękuję Ci! –