2012-07-11 3 views
11

Próbuję napisać menedżera kontekstów, który używa innych menedżerów kontekstów, więc klienci nie muszą znać całego przepisu, tylko interfejsu, który prezentuję. Nie mogę tego zrobić, używając @contextmanager - kod po yield nie zostanie wykonany, jeśli przerwie ci wyjątek, więc muszę użyć menedżera opartego na klasach.Pisanie menedżera kontekstu w Pythonie, który sam używa instrukcji o numerze

Oto mały przykład skrypt:

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 "" 

która wyprowadza to:

before 
{} 

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

exit complex three 
after 
{} 

Chcę go do wyjścia to:

before 
{} 

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

exit complex three 
exit two 
exit one 
after 
{} 

Czy istnieje jakiś sposób, aby powiedzieć klasę oparty menedżer kontekstu, aby otoczyć się innymi menedżerami kontekstów?

+2

Byłoby przydatne do określenia wersji Pythona. –

+0

Przepraszam, ale dlaczego miałbyś to zrobić? Wydaje mi się to naturalne, że menedżer kontekstowy oparty na klasach powinien zakończyć działanie na końcu, po wyczyszczeniu jego zależności. –

+0

Kierowanie na python 2.7, przepraszam –

Odpowiedz

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

Czy możesz wskazać/wyjaśnić, co było problemem w kodzie pytającego, aby szybciej zrozumieć, co się dzieje? :) – n611x007

+1

@naxa: spójrz na dwa ostatnie przykłady wyników w pytaniu. Kod w pytaniu tworzy pierwsze wyjście, mój kod w odpowiedzi tworzy drugi (pożądany). W skrócie: najbardziej zagnieżdżony menedżer kontekstów powinien wyjść najwcześniej. – jfs

1

Kłopot z tym, co robisz, jest to, że w użyciu with w wywołaniu __enter__, kiedy wchodzi się z menedżerem kontekstowe pakowy, oboje wejść i następnie pozostawiając zapakowane menedżerów kontekstowych. Jeśli chcesz napisać własny menedżer kontekstu, który wchodzi do opakowanych menedżerów kontekstu po wejściu w opakowanie, a następnie zamyka je po wyjściu, będziesz musiał ręcznie wywołać funkcje menedżerów kontekstu. Prawdopodobnie nadal będziesz musiał się martwić o bezpieczeństwo wyjątków.

2

Piszesz: "Nie mogę tego zrobić za pomocą @contextmanager - kod po wywołaniu yield nie zostanie wykonany, jeśli przerwie ci wyjątek." Jeśli masz kod, który musisz uruchomić, możesz umieścić go w bloku 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!!") 

wyjściowa:

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!! 
Powiązane problemy