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 Controllers), 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 things 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.
 
    