2014-07-08 15 views
10

W jaki sposób należy zarządzać menedżerem kontekstu w innym menedżerze kontekstu w Pythonie?Obsługa wystąpień menedżera kontekstu w innym menedżerze kontekstu

Przykład: załóżmy, że masz klasę A, która działa jako menedżer kontekstu, i klasa B, która działa także jako menedżer kontekstów. Jednak instancje klasy B będą musiały utworzyć wystąpienie klasy A. Przeszedłem przez PEP 343 i to jest rozwiązanie, o którym myślałem:

Czy to właściwe podejście? Czy może brakuje mi gotchów?

Odpowiedz

2

Alternatywnie, można napisać kod tak:

with A() as a: 
    with B(a) as b: 
     # your code here 

Innym rozwiązaniem może chcesz spróbować może być to:

class A: 

    def __init__(self): 
     pass 

    def __enter__(self): 
     return self 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     pass 

class B(A): 

    def __init__(self): 
     super().__init__() 

    def __enter__(self): 
     super().__enter__() 
     return self 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     super().__exit__(exc_type, exc_val, exc_tb) 

Po rozpatrzeniu wyjaśnienie swojej sytuacji, to może być lepsze rozwiązanie:

class Resource: 

    def __init__(self, dependency=None): 
     self.dependency = dependency 
     # your code here 

    def __enter__(self): 
     if self.dependency: 
      self.dependency.__enter__() 
     # your code here 
     return self 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     # your code here 
     if self.dependency: 
      self.dependency.__exit__(exc_type, exc_val, exc_tb) 

Nie jestem pewien, czy następująca implementacja jest poprawny, ale __exit__ musi odpowiednio dbać o wyjątki. Trudno mi sobie wyobrazić, jak rekurencyjnie łączyć wywołania przy prawidłowej obsłudze wyjątków.

class Resource: 

    def __init__(self, dependency=None): 
     self.dependency = dependency 
     self.my_init() 

    def __enter__(self): 
     if self.dependency: 
      self.dependency.__enter__() 
     return self.my_enter() 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     suppress = False 
     try: 
      suppress = self.my_exit(exc_type, exc_val, exc_tb): 
     except: 
      exc_type, exc_val, exc_tb = sys.exc_info() 
     if suppress: 
      exc_type = exc_val = exc_tb = None 
     if self.dependency: 
      suppress = self.dependeny.__exit__(exc_type, exc_val, exc_tb) 
      if not supress: 
       raise exc_val.with_traceback(exc_tb) from None 
     return suppress 

    def my_init(self): 
     pass 

    def my_enter(self): 
     pass 

    def my_exit(self, exc_type, exc_val, exc_tb): 
     pass 
+1

interesujące. To też może działać. Wadą tego jest to, że użytkownik naszej klasy 'B' będzie musiał stworzyć instancję' A' i przekazać ją nam. Co więcej, byłoby to jeszcze bardziej skomplikowane, gdyby łańcuch wymagań był głębszy niż jeden poziom. Ale dobry pomysł na proste przypadki. Dziękuję Ci. – Sahand

+0

@NoctisSkytower Twoje podejście oparte na klasach jest opłacalne tylko wtedy, gdy naprawdę ma sens, by B było pod-klasą A. IMO, że relacja nie powinna być tworzona ściśle, aby zagnieżdżanie jej było łatwiejsze, ponieważ narusza " IS-A "zasada programowania OO. – dano

+0

Zgadzam się z @dano. Jeśli logiczne jest, że 'B' jest podklasą' A', jest to bardzo dobre rozwiązanie. Mój przykład powyżej jest uproszczony. W moim przypadku użycia, to ta sama klasa, która odwołuje się do samych rekurencyjnie wystąpień (trochę jak lista połączona), a więc wszystkie muszą być wydawane rekursywnie. Pomysł dziedziczenia nie zadziałałby tam. – Sahand

1

Jeśli można użyć @contextlib.contextmanager dekorator twoje życie staje się dużo łatwiejsze:

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", c 
      yield [c] 
      print "Exiting cleanly from external_cm_f", c 
     finally: 
      print "Finally external_cm_f", c 


if "__main__" == __name__: 
    with external_cm(): 
     print "Location A" 
    print 
    with external_cm(): 
     print "Location B" 
     raise Exception("Some exception occurs!!") 
Powiązane problemy