Conditionnel avec déclaration en Python

Est-il possible de commencer un bloc de code avec une instruction, mais de façon conditionnelle?

quelque Chose comme:

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

# do nearly the same large block of stuff,
# involving gs or not, depending on needs_with()

pour clarifier, un scénario aurait un bloc encasé dans l'énoncé avec, alors qu'une autre possibilité serait le même bloc, mais pas encasé (i.e., comme si il n'était pas indenté)

les expériences initiales donnent bien sûr des erreurs d'indentation..

39
demandé sur nicole 2015-01-06 19:36:41

4 réponses

si vous voulez éviter de dupliquer du code et utilisez une version de Python antérieure à 3.3 (où contextlib.ExitStack n'est pas disponible), vous pourriez faire quelque chose comme:

class dummy_context_mgr():
    def __enter__(self):
        return None
    def __exit__(self, exc_type, exc_value, traceback):
        return False

ou:

import contextlib

@contextlib.contextmanager
def dummy_context_mgr():
    yield None

et ensuite l'utiliser comme:

with get_stuff() if needs_with() else dummy_context_mgr() as gs:
   # do stuff involving gs or not

vous pouvez alternativement faire get_stuff() retourner différentes choses basées sur needs_with().

36
répondu jamesdlin 2017-07-30 09:53:12

Python 3.3 introduit contextlib.ExitStack pour ce genre de situation. Il vous donne une "pile", à laquelle vous ajoutez le contexte des gestionnaires nécessaire. Dans votre cas, vous devez faire ceci:

from contextlib import ExitStack

with ExitStack() as stack:
    if needs_with():
        gs = stack.enter_context(get_stuff())

    # do nearly the same large block of stuff,
    # involving gs or not, depending on needs_with()

Tout ce qui est entré stack automatiquement exited à la fin de l' with déclaration comme d'habitude. (Si rien n'est saisi, ce n'est pas un problème.) Dans cet exemple, tout ce qui est retournée par get_stuff()exit ed automatiquement.

Si vous avez l'utilisation d'une version antérieure de python, vous pourriez être en mesure d'utiliser le contextlib2 module, mais ce n'est pas la norme. Il rétroporte ceci et d'autres fonctionnalités aux versions précédentes de python. Vous pouvez même faire une importation conditionnelle, si vous aimez cette approche.

35
répondu Mike 2018-07-07 14:49:38

Une option de tiers pour obtenir exactement ce:

https://pypi.python.org/pypi/conditional

from conditional import conditional

with conditional(needs_with(), get_stuff()):
    # do stuff
8
répondu Anentropic 2016-11-09 15:34:54

Vous pouvez utiliser contextlib.nested mettre 0 ou plusieurs gestionnaires de contexte dans un seul with déclaration.

>>> 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 "<stdin>", line 2, in <module>                                   
ValueError: I/O operation on closed file

Cette solution a ses bizarreries et je viens de remarquer que de 2,7 ses obsolète. J'ai écrit mon propre gestionnaire de contexte pour gérer jongler avec plusieurs gestionnaires de contexte. Sa a fonctionné pour moi jusqu'à présent, mais je n'ai pas vraiment considéré bord de conditions

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
4
répondu tdelaney 2015-01-06 17:34:47