2012-09-26 97 views
10

我正在定義一個上下文管理器類,如果在實例化過程中遇到某些條件,我希望能夠跳過代碼塊而不引發異常。例如,跳過-with-block的執行

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

我發現了這一點,但我不太清楚如何理解它,也不知道如何實現它。 http://www.python.org/dev/peps/pep-0377/還有其他更優雅的方式嗎? –

+0

事實上,這是一個PEP(以及對語義變化的討論),這表明它不能在不訴諸改變解釋器行爲的情況下實施。 – nneonneo

+2

癡迷於整潔? :)與A(),B():其中B的__enter__可以引發一些東西似乎對我來說很好。 – swang

回答

8

如果你想使用來自withhacks(專門從AnonymousBlocksInPython)的想法一個特設的解決方案,這將工作:

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 

比較如下:

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

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

這就是我正在尋找的。 tytytytytyty。 –

+1

我明白了,所以它會以某種方式觸發一個TypeError,它被__exit __()方法捕獲和壓制。有趣的工作! –

+0

我在方法__exit __()中添加了一個if循環來檢查類型和值,以便僅抑制由hack引發的異常。 –

1

你想做什麼是不可能的,很遺憾。如果__enter__引發異常,則會在with語句中引發該異常(不調用__exit__)。如果它沒有引發異常,則返回值將被饋送到該塊並執行該塊。

我能想到的最接近的事是一個標誌由塊明確檢查:

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...' 

這也可以讓你在with塊的中間,以模擬正常break聲明籌集Break()

+0

該標誌有效,但我想保留上下文管理器中的所有檢查,並保持代碼塊清潔。如果不可能的話,我可能不得不另外找到另一種方式。非常感謝你! –

10

根據PEP-343,一個with語句轉換來自:

with EXPR as VAR: 
    BLOCK 

到:

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) 

正如你所看到的,沒有什麼明顯的,你可以從通話中做的__enter__()方法可跳過with語句的正文(「BLOCK」)的上下文管理器。

人們已經在諸如withhacks之類的項目中完成了Python實現特定的操作,例如操縱__enter__()內部的調用堆棧。我記得亞歷克斯·馬爾泰利在一兩年後發佈了一個非常有趣的黑客攻擊(不要回憶足夠的帖子來搜索並找到它)。

但是,對於你的問題/問題的簡單回答是,你不能做你在問什麼,跳過with語句的主體,而不訴諸所謂的「深魔法」(它不一定是可移植的python之間實現)。有了很深的魔法,你可能會做到這一點,但我建議只做一些練習,看看它是如何完成的,而不是在「生產代碼」中。

+1

哦,我的上帝'withhacks'是瘋了,他們在Python中製作了Ruby風格的塊... – nneonneo

+0

好的,這解釋了很多。我檢查了與黑客。在這一點上認爲它超出了我的範圍......不知道我該如何使用代碼來執行跳過,但是肯定有一些我可以使用的有趣的代碼片段。 (更新:RUBY STYLE BLOCKS?我看到了,哈哈哈,確實是瘋了) 否則,我真的需要考慮另一種方法。謝謝! –