The pattern using with
together with open()
to automatically close a file after leaving the context is well known. To open a fixed number you can simply nest these statements or use the comma notation. For having a context which represents an arbitrary number of open files you can use the ExitStack
class, but only for Python 3.3+.
For other Python versions I'm using the following class which I named Files
. The presented implementation is only for reading files (for keeping it clear). Extending it for having various file modes should not pose a problem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Files(tuple):
def __new__(cls, *filePaths):
files = []
try:
for filePath in filePaths:
files.append(open(filePath))
files[-1].__enter__()
except:
for file in files:
file.close()
raise
else:
return super(Files, cls).__new__(cls, files)
def __enter__(self):
return self
def __exit__(self, *args):
for file in self:
file.close()
|
A typical use looks like this:
with Files('.bashrc', '.profile') as (a, b):
print a.read()
print b.read()
Or, more complex: This mixes the contents of the given files in a round-robin fashion and continues reading from them like tail -f
afterwards (useful to keep an eye on several growing logfiles at once). This will terminate as soon as in one file a line starting with END
appears.
filePaths = argv[1:]
with Files(*filePaths) as files:
while True:
filesExhausted = True
for file in files:
line = file.readline()
if line is not None:
filesExhausted = False
sys.stdout.write(line)
if line.startswith('END'):
return # or whatever leaves the outer loop
if filesExhausted:
time.sleep(1.0)