[Python-Dev] Multiline with statement line continuation (original) (raw)

Nick Coghlan ncoghlan at gmail.com
Wed Aug 13 10:34:58 CEST 2014


On 12 August 2014 22:15, Steven D'Aprano <steve at pearwood.info> wrote:

Compare the natural way of writing this:

with open("spam") as spam, open("eggs", "w") as eggs, frobulate("cheese") as cheese: # do stuff with spam, eggs, cheese versus the dynamic way: with ExitStack() as stack: spam, eggs = [stack.entercontext(open(fname), mode) for fname, mode in zip(("spam", "eggs"), ("r", "w")] cheese = stack.entercontext(frobulate("cheese")) # do stuff with spam, eggs, cheese

You wouldn't necessarily switch at three. At only three, you have lots of options, including multiple nested with statements:

with open("spam") as spam:
    with open("eggs", "w") as eggs:
        with frobulate("cheese") as cheese:
            # do stuff with spam, eggs, cheese

The "multiple context managers in one with statement" form is there solely to save indentation levels, and overuse can often be a sign that you may have a custom context manager trying to get out:

@contextlib.contextmanager
def dish(spam_file, egg_file, topping):
    with open(spam_file), open(egg_file, 'w'), frobulate(topping):
        yield

with dish("spam", "eggs", "cheese") as spam, eggs, cheese:
    # do stuff with spam, eggs & cheese

ExitStack is mostly useful as a tool for writing flexible custom context managers, and for dealing with context managers in cases where lexical scoping doesn't necessarily work, rather than being something you'd regularly use for inline code.

"Why do I have so many contexts open at once in this function?" is a question developers should ask themselves in the same way its worth asking "why do I have so many local variables in this function?"

Regards, Nick.

-- Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia



More information about the Python-Dev mailing list