2012-07-11 32 views
11

我想要編寫使用其他上下文管理上下文管理,因此客戶端不需要知道整個食譜,只是我提出的接口。我無法使用@contextmanager - 如果由於異常而中斷yield調用後的代碼不會執行,所以我需要使用基於類的管理器。寫一個上下文管理器在Python本身使用with語句

這裏有一個小例子腳本:

from contextlib import contextmanager 
import pprint 

d = {} 

@contextmanager 
def simple(arg, val): 
    print "enter", arg 
    d[arg] = val 
    yield 
    print "exit", arg 
    del d[arg] 

class compl(object): 
    def __init__(self, arg, val): 
     self.arg=arg 
     self.val=val 

    def __enter__(self): 
     with simple("one",1): 
      with simple("two",2): 
       print "enter complex", self.arg 
       d[self.arg] = self.val 

    def __exit__(self,*args): 
     print "exit complex", self.arg 
     del d[self.arg] 

print "before" 
print d 
print "" 

with compl("three",3): 
    print d 
    print "" 

print "after" 
print d 
print "" 

輸出這樣的:

before 
{} 

enter one 
enter two 
enter complex three 
exit two 
exit one 
{'three': 3} 

exit complex three 
after 
{} 

我希望它輸出這樣的:

before 
{} 

enter one 
enter two 
enter complex three 
{'one': 1, 'three': 3, 'two': 2} 

exit complex three 
exit two 
exit one 
after 
{} 

有沒有辦法告訴類基於上下文的管理器用其他上下文管理器來包裝自己?

+2

這將會是指定的Python版本是有用的。 – 2012-07-11 20:40:39

+0

不好意思,但你爲什麼要這麼做?對於我來說,基於類的上下文管理器在清除其依賴關係之後應該最後退出,這似乎很自然。 – 2012-07-11 20:44:07

+0

Targeting python 2.7,sorry – 2012-07-12 02:49:13

回答

12
@contextmanager 
def compl(arg, val): 
   with simple("one",1): 
        with simple("two",2): 
      print "enter complex", arg 
      try: 
                d[arg] = val 
       yield 
      finally: 
          del d[arg] 
          print "exit complex", arg 
+3

你能指出/解釋提問者代碼中的問題是什麼,因此理解發生的事情會更快嗎? :) – n611x007 2013-10-24 15:56:33

+1

@naxa:看問題中的最後兩個輸出示例。問題中的代碼產生第一個輸出,我的代碼在答案中產生第二個(合意的)。簡而言之,最嵌套的上下文管理器應該最快退出。 – jfs 2013-10-24 16:08:18

1

與你在做什麼麻煩的是,在您的通話__enter__使用with,當你進入你的包裝方面的經理,你在進入和離開,然後包裹上下文管理。如果你想編寫自己的上下文管理器,當你進入包裝器時進入包裝的上下文管理器,然後在你離開時退出它們,你必須手動調用包裝上下文管理器的功能。您可能還需要擔心異常安全。

2

你寫的,「使用@contextmanager我不能做到這一點 - 產量調用之後的代碼,如果你是一個異常中斷,沒有得到執行」如果你有必須運行的代碼,你可以把它放在一個try/finally塊中。

import contextlib 

@contextlib.contextmanager 
def internal_cm(): 
    try: 
     print "Entering internal_cm" 
     yield None 
     print "Exiting cleanly from internal_cm" 
    finally: 
     print "Finally internal_cm" 

@contextlib.contextmanager 
def external_cm(): 
    with internal_cm() as c: 
     try: 
      print "In external_cm_f" 
      yield [c] 
      print "Exiting cleanly from external_cm_f" 
     finally: 
      print "Finally external_cm_f" 

if "__main__" == __name__: 
    with external_cm() as foo1: 
     print "Location A" 
    print 
    with external_cm() as foo2: 
     print "Location B" 
     raise Exception("Some exception occurs!!") 

輸出:

Entering internal_cm 
In external_cm_f 
Location A 
Exiting cleanly from external_cm_f 
Finally external_cm_f 
Exiting cleanly from internal_cm 
Finally internal_cm 

Entering internal_cm 
In external_cm_f 
Location B 
Finally external_cm_f 
Finally internal_cm 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "C:\Anaconda\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 540, in runfile 
    execfile(filename, namespace) 
    File "C:\untitled0.py", line 35, in <module> 
    raise Exception("Some exception occurs!!") 
Exception: Some exception occurs!! 
相關問題