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..
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()
.
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 exit
ed à 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.
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
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