Reputation: 386
I want to be able to do something like this:
file1 = 'data/f1.txt'
file2 = None
file3 = 'data/f3.txt'
with open(file1) as f1, open(file2) as f2, open(file3) as f3:
for i in range(1000):
x,y,z = func(i)
if f1: f1.write(f"{x}\n")
if f2: f2.write(f"{y}\n")
if f3: f3.write(f"{z}\n")
So that output is only written to files if paths are provided.
I tried the following:
file1 = 'data/f1.txt'
file2 = None
file3 = 'data/f3.txt'
with open(file1) if file1 else None as f1, open(file2) if file2 else None as f2, open(file3) if file3 else None as f3:
for i in range(1000):
x,y,z = func(i)
if f1: f1.write(f"{x}\n")
if f2: f2.write(f"{y}\n")
if f3: f3.write(f"{z}\n")
But I got TypeError: expected str, bytes or os.PathLike object, not NoneType
.
Is there a simple, pythonic way to achieve this?
Thanks.
Upvotes: 0
Views: 66
Reputation: 20450
By the time you've invented identifiers file{1,2,3}
,
that's starting to be a Code Smell that you
really want to fill a container with files.
Create a class FilesWriter
that is a context manager.
Pass in a bunch of filespecs.
It will simply discard any empty ones:
self.fspec_to_file = {}
for fspec in fspecs:
if fspec is not None:
self.fspec_to_file[fspec] = open(fspec, 'w')
Now your instance is responsible for closing those
when the with
calls your __exit__
method.
In the body of the with
,
caller will either invoke a write_to_all(s: str)
method you provide,
or will use the elements of fspec_to_file
as needed.
So calling sequence would look like:
with FilesWriter([fspec1, fspec2, fspec3]) as fw:
for i in range(1000):
result = compute()
fw.write_to_all(result)
and upon leaving the block your __exit__
method does:
for f in self.fspec_to_file.values():
f.close()
Strictly speaking .close()
can fail, e.g. due to full disk.
Put it in a try
block if needed.
If any open()
fails, it might make sense to
close all previously opened files and die
with fatal error.
You seemed to need to know details about each open file.
But if not, feel free to convert that dict
to
a simple set
of open file handles.
Upvotes: 2