views:

1241

answers:

5

Is it possible to declare more than one variable using a with statement in Python?

Something like:

from __future__ import with_statement

with open("out.txt","wt"), open("in.txt") as file_out, file_in:
    for line in file_in:
        file_out.write(line)

... or is cleaning up two resources at the same time the problem?

+7  A: 

I think you want to do this instead:

from __future__ import with_statement

with open("out.txt","wt") as file_out:
    with open("in.txt") as file_in:
        for line in file_in:
            file_out.write(line)
Andrew Hare
That's how I currently do it, but then the nesting is twice as deep as I want (mean) it to be...
pufferfish
I think this is the cleanest approach though - any other approach is going to be harder to read. Alex Martelli's answer seems to be closer to what you want but is much less readable. Why is nesting such a concern?
Andrew Hare
Not a big deal, admittedly, but, per "import this" (aka "Zen of Python"), "flat is better than nested" -- that's why we added contextlib.nested to the standard library. BTW, 3.1 might have a new syntax "with A() as a, B() as b:" (the patch is in, no BDFL pronouncement about it so far though) for more direct support (so clearly the library solution isn't considered perfect... but avoiding unwanted nesting is definitely a widely shared goal among core Python developers).
Alex Martelli
@Alex: Very true but we must also consider that "Readability counts".
Andrew Hare
@Andrew: I think one level of indentation better expresses the intended logic of the program, which is to "atomically" create two variables, and clean them up later together (I realise this isn't actually what happens). Think the exception issue is a deal breaker though
pufferfish
+14  A: 

See http://docs.python.org/library/contextlib.html -- contextlib.nested supports this use. I.e.:

import contextlib

with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in):

   ...

which is very close to your desired usage.

Alex Martelli
I am sorry to say that, but I think that the `nested` context manager is a mistake and should never be used. In this example, if opening the second file raises an exception, the first file won't be closed at all, thus totally destroying the purpose of using context managers.
Rafał Dowgird
Why do you say that? The documentation says that using nested is equivalent to nested 'with's
James Hopkin
@Rafal: a glance at the manual seems to indicate that python properly nests the with statements. The real problem is if the second file throws an exception upon closing.
Unknown
@Unknown: that's my reading too - and as far as I know, File object won't raise an exception on closing
James Hopkin
@James: No, the equivalent code in the docs at http://docs.python.org/library/contextlib.html#contextlib.nested differs from the standard nested `with` blocks. The managers are created in order *before* entering the with blocks:m1, m2, m3 = A(), B(), C()If B() or C() fails with exception, then your only hope of properly finalizing A() is the garbage collector.
Rafał Dowgird
A: 

why not just:

with open(out_file, 'w') as file:
    file.writelines(open(in_file))
SilentGhost
Because the in_file won't get closed.
Rafał Dowgird
@Rafał Dowgird: Yes it will.
nosklo
Depends on the Python implementation (as nosklo hints at in his answer). It relies on finalisers being called for the files.
James Hopkin
@James Hopkin: I don't know about any implementation that does not close the files. Do you?
nosklo
@nosklo, any python implementation that does not implement reference counting: ie Jython IronPython, potentially could leave the file open longer than the block.
Unknown
@unknow: do these implementation have a with statement? or contextlib?
SilentGhost
@nosklo: If you want to rely on finalizers (which is bad practice anyway). But then why bother with enclosing the `out_file` in the with block?
Rafał Dowgird
@Rafal Dowgird: Note that in my answer, I didn't.
nosklo
+1  A: 
open('out.txt', 'w').writelines(open('in.txt'))

Both files will be implicity, immediately closed just after execution of this line, in CPython.

nosklo
+14  A: 

It is possible now, in Python 3.1. The new with syntax supports multiple context managers:

with A() as a, B() as b, C() as c:
    doSomething(a,b,c)

Unlike the contextlib.nested, this guarantees that a and b will have their __exit__()'s called even if C() or it's __enter__() method raises an exception.

Update: Now coming to a Python 2.7 interpreter near you!

Rafał Dowgird
+1 for delivering the good news!
pufferfish
+1 ... not every Python answer by Alex Martelli is the best IMO.
Hamish Grubijan