2014-07-08 6 views
10

다른 컨텍스트 관리자 내부에서 작성된 컨텍스트 관리자는 어떻게 파이썬에서 처리해야합니까?다른 컨텍스트 관리자 내부의 컨텍스트 관리자 인스턴스 처리

예 : 컨텍스트 관리자 역할을하는 A 클래스와 컨텍스트 관리자 역할을하는 B 클래스가 있다고 가정합니다. 그러나 클래스 B 인스턴스는 인스턴스화하고 클래스 A의 인스턴스를 사용해야합니다. 저는 PEP 343을 생각해 봤는데 이것이 제가 생각한 해결책입니다 :

class A(object): 
    def __enter__(self): 
     # Acquire some resources here 
     return self 

    def __exit__(seplf, exception_type, exception, traceback): 
     # Release the resources and clean up 
     pass 


class B(object): 
    def __init__(self): 
     self.a = A() 

    def __enter__(self): 
     # Acquire some resources, but also need to "start" our instance of A 
     self.a.__enter__() 
     return self 

    def __exit__(self, exception_type, exception, traceback): 
     # Release the resources, and make our instance of A clean up as well 
     self.a.__exit__(exception_type, exception, traceback) 

올바른 방법입니까? 아니면 내가 몇 가지 잡았을까?

답변

2

또는, 당신은과 같이 코드를 작성할 수

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

이 될 수 시도 할 수 있습니다 또 다른 방법 : 상황의 설명을 고려한 후에

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) 

을,이있을 수 있습니다 더 나은 해결책 :

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) 

다음 구현 정확하지만, __exit__은 예외를 적절하게 처리해야합니다. 예외를 적절하게 처리하면서 호출을 순환 적으로 연결하는 방법을 상상하는 것은 다소 어렵습니다.

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

재미있는합니다. 그것도 작동 할 수 있습니다. 이것의 단점은'B' 클래스의 사용자가'A'의 인스턴스를 만들어서 우리에게 넘겨 주어야한다는 것입니다. 또한 요구 사슬이 한 레벨 이상 깊어지면 점점 더 복잡해질 것입니다. 그러나 간단한 경우에는 좋은 아이디어입니다. 고맙습니다. – Sahand

+0

@NoctisSkytower 클래스 기반 접근 방식은 B가 A. 하위 클래스가되는 것이 실제로 의미가있는 경우에만 실행 가능합니다. IMO는 컨텍스트 관리자로 중첩되도록 엄격하게 생성해서는 안되며, IS-A "OO 프로그래밍 원칙을 준수해야합니다. – dano

+0

나는 @dano에 동의한다. 논리적으로'B'가'A'의 하위 클래스라는 것이 합리적이라면 이것은 아주 좋은 해결책입니다. 위의 예는 지나치게 단순화 된 것입니다. 내 실제 사용 케이스에서 그것은 반복적으로 (연결된 목록과 같이) 자체적으로 인스턴스에 대한 참조를 갖는 동일한 클래스이므로 모두 재귀 적으로 릴리스해야합니다. 상속 아이디어는 거기서 작동하지 않습니다. – Sahand

1

당신이 당신의 인생을 훨씬 쉽게 얻는 @contextlib.contextmanager 장식 사용할 수있는 경우 :

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