How to check if an object is created with `with` statement?

前端 未结 6 1097
太阳男子
太阳男子 2021-01-02 02:15

I would like to ensure that the class is only instantiated within a \"with\" statement.

i.e. this one is ok:

with X() as x:
 ...

an

6条回答
  •  暗喜
    暗喜 (楼主)
    2021-01-02 02:48

    There is no straight forward way, as far as I know. But, you can have a boolean flag, to check if __enter__ was invoked, before the actual methods in the objects were called.

    class MyContextManager(object):
    
        def __init__(self):
            self.__is_context_manager = False
    
        def __enter__(self):
            print "Entered"
            self.__is_context_manager = True
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
            print "Exited"
    
        def do_something(self):
            if not self.__is_context_manager:
                raise Exception("MyContextManager should be used only with `with`")
    
            print "I don't know what I am doing"
    

    When you use it with with,

    with MyContextManager() as y:
        y.do_something()
    

    you will get

    Entered
    I don't know what I am doing
    Exited
    

    But, when you manually create an object, and invoke do_something,

    x = MyContextManager()
    x.do_something()
    

    you will get

    Traceback (most recent call last):
      File "/home/thefourtheye/Desktop/Test.py", line 22, in 
        x.do_something()
      File "/home/thefourtheye/Desktop/Test.py", line 16, in do_something
        raise Exception("MyContextManager should be used only with `with`")
    Exception: MyContextManager should be used only with `with`
    

    Note: This is not a solid solution. Somebody can directly invoke __enter__ method alone, before calling any other methods and the __exit__ method may never be called in that case.

    If you don't want to repeat that check in every function, you can make it a decorator, like this

    class MyContextManager(object):
    
        def __init__(self):
            self.__is_context_manager = False
    
        def __enter__(self):
            print "Entered"
            self.__is_context_manager = True
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
            print "Exited"
    
        def ensure_context_manager(func):
            def inner_function(self, *args, **kwargs):
                if not self.__is_context_manager:
                    raise Exception("This object should be used only with `with`")
    
                return func(self, *args, **kwargs)
            return inner_function
    
        @ensure_context_manager
        def do_something(self):
            print "I don't know what I am doing"
    

提交回复
热议问题