Search code examples
pythonfileioconditional-statementswith-statement

Open multiple files based on conditionals using a single with statement


I want to open multiple files using a with statement (so I get the benefit of the context manager) based on boolean flags which instruct whether my program should or should not actually open each file.

I know I can use a with statement to open multiple files, like:

with open('log.txt', 'w') as logfile, open('out_a.txt', 'w') as out_a, open('out_b.txt', 'w') as out_b:
    # do something with logfile, out_a and out_b
# all files are closed here

I want to run a similar statement, but only opening certain files based on their corresponding flags. I thought about implementing it as a conditional_openfunction, something like:

write_log = True
write_out_a = False
write_out_b = True

with conditional_open('log.txt', 'w', cond=write_log) as logfile, open('out_a.txt', 'w', cond=write_out_a) as out_a, open('out_b.txt', 'w', cond=write_out_b) as out_b:
    # do something with logfile, out_a and out_b
# all files are closed here

But I'm a little confused as to how properly create that function. Ideally, coditional_open would either return an open file handle or None (in which case the file is never created/touched/deleted):

def conditional_open(filename, mode, cond):
    return open(filename, mode) if cond else None

But I fear that this skips the benefits of the context manager when opening a file, since I'm calling open outside from it. Is this assumption correct?

Can anyone give some ideas about how I could be doing this? I know I could create mock file objects based on the conditions and write to them instead, but it sounds a bit too convoluted to me - this seems like a simple problem, which should have a simple solution in Python.


Solution

  • Just set up your function as a context manager.

    from contextlib import contextmanager
    
    @contextmanager
    def conditional_open(f_name, mode, cond):
        if not cond:
            yield None
        resource = open(f_name, mode)
        try:
            yield resource
        finally:
            resource.close()