Conditional with statement in Python

前端 未结 8 1355
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-28 06:04

Is there a way to begin a block of code with a with statement, but conditionally?

Something like:

if needs_with():
    with get_stuff() as gs:

# do          


        
8条回答
  •  余生分开走
    2020-11-28 06:15

    You can use contextlib.nested to put 0 or more context managers into a single with statement.

    >>> import contextlib
    >>> managers = []
    >>> test_me = True
    >>> if test_me:
    ...     managers.append(open('x.txt','w'))
    ... 
    >>> with contextlib.nested(*managers):                                                       
    ...  pass                                                    
    ...                                                             
    >>> # see if it closed
    ... managers[0].write('hello')                                                                                                                              
    Traceback (most recent call last):                              
      File "", line 2, in                                    
    ValueError: I/O operation on closed file
    

    This solution has its quirks and I just noticed that as of 2.7 its been deprecated. I wrote my own context manager to handle juggling multiple context managers. Its worked for me so far, but I haven't really considered edge conditons

    class ContextGroup(object):
        """A group of context managers that all exit when the group exits."""
    
        def __init__(self):
            """Create a context group"""
            self._exits = []
    
        def add(self, ctx_obj, name=None):
            """Open a context manager on ctx_obj and add to this group. If
            name, the context manager will be available as self.name. name
            will still reference the context object after this context
            closes.
            """
            if name and hasattr(self, name):
                raise AttributeError("ContextGroup already has context %s" % name)
            self._exits.append(ctx_obj.__exit__)
            var = ctx_obj.__enter__()
            if name:
                self.__dict__[name] = var
    
        def exit_early(self, name):
            """Call __exit__ on named context manager and remove from group"""
            ctx_obj = getattr(self, name)
            delattr(self, name)
            del self._exits[self._exits.index(ctx_obj)]
            ctx_obj.__exit__(None, None, None)
    
        def __enter__(self):
            return self
    
        def __exit__(self, _type, value, tb):
            inner_exeptions = []
            for _exit in self._exits:
                try:
                    _exit(_type, value, tb )
                except Exception, e:
                    inner_exceptions.append(e)
            if inner_exceptions:
                r = RuntimeError("Errors while exiting context: %s" 
                    % (','.join(str(e)) for e in inner_exceptions))
    
        def __setattr__(self, name, val):
            if hasattr(val, '__exit__'):
                self.add(val, name)
            else:
                self.__dict__[name] = val
    

提交回复
热议问题