How many variables are in the 'with' statement?

Posted by Spitfire on Sat, 07 Mar 2020 13:59:22 +0100

Can I declare multiple variables using the with statement in Python?

It's 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 are the two resources being cleaned up and having problems at the same time?

#1 building

From v3.1 and Python 2.7 At first, It can be in Implementation in Python 3 . new with syntax Multiple context managers are supported:

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

Unlike contextlib.nested, this ensures that a and b will have their own call to \\\\\\\\\\\.

#2 building

Because Python 3.3, you can use classes ExitStack from contextlib Modular.

It manages a dynamic number of context aware objects, which means it's particularly useful if you don't know how many files to process.

The specification use case mentioned in the document is to manage a dynamic number of files.

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

This is a general example:

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(stack._exit_callbacks)
    nums = [stack.enter_context(x) for x in xs]
    print(stack._exit_callbacks)
print(stack._exit_callbacks)
print(nums)

Output:

deque([])
enter X1
enter X2
enter X3
deque([<function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86158>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f861e0>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86268>])
exit X3
exit X2
exit X1
deque([])
[1, 2, 3]

#3 building

Note that if you split a variable into rows, you must use a backslash to wrap the line.

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

Parentheses don't work because Python creates a tuple.

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

An error (unrecognized and unrecognized class type) occurs because the tuple is missing the 'enter' attribute:

AttributeError: __enter__

If you try to use as brackets, python catches errors in analysis:

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

Syntax error: invalid syntax

https://bugs.python.org/issue12782 It seems to have something to do with this problem.

#4 building

I think you want to do this:

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)

#5 building

contextlib.nested Support this:

import contextlib

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

   ...

To update:
Reference documents, about contextlib.nested :

Not recommended since version 2.7: the with statement now directly supports this feature (no error prone quirks).

For more information, see Rafa fongird's answer .

Topics: Python Attribute