<p><br>
On Aug 13, 2014 7:04 PM, "Akira Li" <<a href="mailto:4kir4.1i@gmail.com">4kir4.1i@gmail.com</a>> wrote:<br>
><br>
> Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>> writes:<br>
><br>
> > On 12 August 2014 22:15, Steven D'Aprano <<a href="mailto:steve@pearwood.info">steve@pearwood.info</a>> wrote:<br>
> >> Compare the natural way of writing this:<br>
> >><br>
> >> with open("spam") as spam, open("eggs", "w") as eggs, frobulate("cheese") as cheese:<br>
> >>     # do stuff with spam, eggs, cheese<br>
> >><br>
> >> versus the dynamic way:<br>
> >><br>
> >> with ExitStack() as stack:<br>
> >>     spam, eggs = [stack.enter_context(open(fname), mode) for fname, mode in<br>
> >>                   zip(("spam", "eggs"), ("r", "w")]<br>
> >>     cheese = stack.enter_context(frobulate("cheese"))<br>
> >>     # do stuff with spam, eggs, cheese<br>
> ><br>
> > You wouldn't necessarily switch at three. At only three, you have lots<br>
> > of options, including multiple nested with statements:<br>
> ><br>
> >     with open("spam") as spam:<br>
> >         with open("eggs", "w") as eggs:<br>
> >             with frobulate("cheese") as cheese:<br>
> >                 # do stuff with spam, eggs, cheese<br>
> ><br>
> > The "multiple context managers in one with statement" form is there<br>
> > *solely* to save indentation levels, and overuse can often be a sign<br>
> > that you may have a custom context manager trying to get out:<br>
> ><br>
> >     @contextlib.contextmanager<br>
> >     def dish(spam_file, egg_file, topping):<br>
> >         with open(spam_file), open(egg_file, 'w'), frobulate(topping):<br>
> >             yield<br>
> ><br>
> >     with dish("spam", "eggs", "cheese") as spam, eggs, cheese:<br>
> >         # do stuff with spam, eggs & cheese<br>
> ><br>
> > ExitStack is mostly useful as a tool for writing flexible custom<br>
> > context managers, and for dealing with context managers in cases where<br>
> > lexical scoping doesn't necessarily work, rather than being something<br>
> > you'd regularly use for inline code.<br>
> ><br>
> > "Why do I have so many contexts open at once in this function?" is a<br>
> > question developers should ask themselves in the same way its worth<br>
> > asking "why do I have so many local variables in this function?"<br>
><br>
> Multiline with-statement can be useful even with *two* context<br>
> managers. Two is not many.<br>
><br>
> Saving indentations levels along is a worthy goal. It can affect<br>
> readability and the perceived complexity of the code.<br>
><br>
> Here's how I'd like the code to look like:<br>
><br>
>   with (open('input filename') as input_file,<br>
>         open('output filename', 'w') as output_file):<br>
>       # code with list comprehensions to transform input file into output file<br>
><br>
> Even one additional unnecessary indentation level may force to split<br>
> list comprehensions into several lines (less readable) and/or use<br>
> shorter names (less readable). Or it may force to move the inline code<br>
> into a separate named function prematurely, solely to preserve the<br>
> indentation level (also may be less readable) i.e.,<br>
><br>
>   with ... as input_file:<br>
>       with ... as output_file:<br>
>           ... #XXX indentation level is lost for no reason<br>
><br>
>   with ... as infile, ... as outfile: #XXX shorter names<br>
>       ...<br>
><br>
>   with ... as input_file:<br>
>       with ... as output_file:<br>
>           transform(input_file, output_file) #XXX unnecessary function<br>
><br>
> And (nested() can be implemented using ExitStack):<br>
><br>
>   with nested(open(..),<br>
>               open(..)) as (input_file, output_file):<br>
>       ... #XXX less readable<br>
><br>
> Here's an example where nested() won't help:<br>
><br>
>   def get_integers(filename):<br>
>       with (open(filename, 'rb', 0) as file,<br>
>             mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as mmapped_file):<br>
>           for match in re.finditer(br'\d+', mmapped_file):<br>
>               yield int(match.group())<br>
><br>
> Here's another:<br>
><br>
>   with (open('log'+'some expression that generates filename', 'a') as logfile,<br>
>         redirect_stdout(logfile)):<br>
>       ...<br>
><br>
Just a thought, would it bit wierd that:<br>
with (a as b, c as d): "works"<br>
with (a, c): "boom"<br>
with(a as b, c): ?</p>
<p>><br>
> --<br>
> Akira<br>
><br>
> _______________________________________________<br>
> Python-Dev mailing list<br>
> <a href="mailto:Python-Dev@python.org">Python-Dev@python.org</a><br>
> <a href="https://mail.python.org/mailman/listinfo/python-dev">https://mail.python.org/mailman/listinfo/python-dev</a><br>
> Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/yoavglazner%40gmail.com">https://mail.python.org/mailman/options/python-dev/yoavglazner%40gmail.com</a><br>
</p>