I've got a situation where I have several items I'd like to open using a with
block. In my case, these are external hardware devices which require some clean-up when closed -- but that doesn't really matter for the point at hand.
Assuming a class something like:
class Controller(object):
def __init__(self, name):
self._name = name
def __enter__(self):
# Do some work on entry
print("Entering", self._name)
return self
def __exit__(self, type, value, traceback):
# Clean up (restoring external state, turning off hardware, etc)
print("Exiting", self._name)
return False
def work(self):
print("Working on", self._name)
I would (given a fixed number of Controller
s), do something like
with Controller("thing1") as c1:
with Controller("thing2") as c2:
c1.do_work()
c2.do_work()
However, I've run across a situation where I have a flexible number of things I need to manage in this manner. That is, I have a situation similar to:
things = ["thing1", "thing2", "thing3"] # flexible in size
for thing in things:
with Controller(thing) as c:
c.do_work()
However, the above doesn't quite do what I need -- which is to have Controllers
for all thing
s in scope at one time.
I've built a toy example which works through recursion:
def with_all(controllers, f, opened=None):
if opened is None:
opened = []
if controllers:
with controllers[0] as t:
opened.append(t)
controllers = controllers[1:]
with_all(controllers, f, opened)
else:
f(opened)
def do_work_on_all(controllers):
for c in controllers:
c.work()
names = ["thing1", "thing2", "thing3"]
controllers = [Controller(n) for n in names]
with_all(controllers, do_work_on_all)
but I don't like the recursion or the abstraction of the actual function call. I'm interested in ideas for doing this in a more "pythonic" way.
Yes there is a more pythonic way to do that, using the standard library contextlib, which has a class ExitStack which does excatly what you want:
with ExitStack() as stack:
controllers = [stack.enter_context(Controller(n)) for n in names]
This should do what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With