I am interested in doing a list comprehension inside a Python with
statement, so that I can open multiple context managers at the same time with minimal syntax. I am looking for answers that work with Python 2.7 .
Consider the following code example. I want to use the with
statement on variables in an arbitrarily-long list at the same time , preferably in a syntactically-clean fashion.
def do_something(*args):
contexts = {}
with [open(arg) as contexts[str(i)] for i, arg in enumerate(args)]:
do_another_thing(contexts)
do_something("file1.txt", "file2.txt")
Does anybody know if there is a way to involve a list comprehension inside of a with
statement in Python 2.7 ?
Here are some things I've already looked at, with an explanation of why they do not suit my purposes:
For Python 2.6- , I could use contextlib.nested
to accomplish this a bit like:
def do_something(*args):
contexts = {}
with nested(*[open(arg) for arg in args]) as [contexts[str(i)] for i in range(len(args))]:
do_another_thing(contexts)
However, this is deprecated in Python 2.7+ , so I am assuming it is bad practice to use.
Instead, the new syntax was given on this SO answer , as well as this SO answer :
with A() as a, B() as b, C() as c:
doSomething(a,b,c)
However, I need to be able to deal with an arbitrary input list, as in the example I gave above. This is why I favour list comprehension.
For Python 3.3+ , this SO answer described how this could be accomplished by using ExitStack
. However, I am working in Python 2.7.
There is also this solution , but I would prefer to not write my own class to accomplish this.
Is there any hope of combining a list comprehension and a with
statement in Python 2.7?
Update 1-3: Updated example to better emphasize the functionality I am looking for
Update 4: Found another similar question . This one has an answer which also suggests ExitStack
, a function that is not available in 2.7.
The major tasks of with
statement is invoking the __exit__
attribute of its context manager. Specially for dealing with files. So In this case due to this point and the fact that open()
returns an file_object
you can dimple use a list comprehension in order to create the list of your file objects and just call the exit()/close()
manually. But be aware that in this case you have to handle the exceptions manually.
def print_files(*args):
f_objs = [open(arg) for arg in args]
# do what you want with f_objs
# ...
# at the end
for obj in f_objs:
f.close()
Note that if you only want to run some parallel operations on your file objects, I recommend this approach, other wise the best way is using a with
statement inside a for loop, and opennning the files in each iteration (on names) like following:
for arg in args:
with open(arg) as f:
# Do something with f
For more safety you can use a custom open
function in order to handle the exceptions too:
def my_open(*args, **kwargs):
try:
file_obj = open(*args, **kwargs)
except Exception as exp:
# do something with exp and return a proper object
else:
return file_obj
def print_files(*args):
f_objs = [my_open(arg) for arg in args]
# do what you want with f_objs
# ...
# at the end
for obj in f_objs:
try:
f.close()
except Exception as exp:
# handle the exception
Doing this yourself is really tricky, especially handling exceptions that occur while opening or closing the files. I'd recommend just getting a library like contextlib2
that implements the contextlib.ExitStack
functionality. Then you can do
with contextlib2.ExitStack() as stack:
files = [stack.enter_context(open(arg)) for arg in args]
...
just like you were using contextlib.ExitStack
from Python 3, and everything is handled correctly for you.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.