最初,我一直在打开并同时读取两个文件,如下所示:
with open(file1, 'r') as R1:
with open(file2, 'r') as R2:
### my code
但是现在输入文件名有时可能会被压缩。因此,我想将with
语句拆分并使用if
语句来处理两种情况,如下所示:
if zipped:
R1 = gzip.open(file1, 'r')
R2 = gzip.open(file2, 'r')
else:
R1 = open(file1, 'r')
R2 = open(file2, 'r')
with R1:
with R2:
### my code
第二个代码是否像第一个一样起作用?还是有更好的方法呢?
答案 0 :(得分:5)
您在做什么主要是是有道理的,但这有一个问题。
文件对象是上下文管理器,它们close
本身位于__exit__
上。正如gzip
文档明确指出的那样,其中包括GzipFile
返回的gzip.open
个对象:
GzipFile
支持io.BufferedIOBase
接口,包括迭代和with
语句。
因此,如果在打开的常规文件或with f:
上写入GzipFile
,则可以保证在close
语句后调用with
。
在Python 2.7中,细节稍有不同,但工作方式相同。在Python 2.6中,GzipFile
不是上下文管理器。但是有一个非常简单的解决方案(即使您不关心Python 2.6也值得了解其他类型):您可以在closing
中使用close
方法包装任何内容以获得上下文管理器在close
上调用__exit__
。因此,您可以这样写:
with contextlib.closing(R1):
...,无论它是文件对象还是其他不知道如何成为上下文管理器的东西(例如2.6 R1
),它都可以在GzipFile
上运行。
但是,如果R1
成功打开但R2
失败,会发生什么?这样,当引发异常时,您甚至都没有进入with R1:
,因此您永远不会关闭R1
。
您可以通过在打开with R1
之前执行R2
来解决此问题:
if zipped:
R1 = gzip.open(file1, 'r')
else:
R1 = open(file1, 'r')
with R1:
if zipped:
R2 = gzip.open(file2, 'r')
else:
R2 = open(file2, 'r')
with R2:
或者您可以使用ExitStack
。
但是这里有一个更简单的解决方案:gzip.open
和open
都是可调用的对象,因此您可以将它们存储在变量中,然后再调用它。由于它们具有相同的签名,并且您希望使用完全相同的参数来调用它们,因此使用该变量很简单:
if zipped:
zopen = gzip.open
else:
zopen = open
with zopen(file1, 'r') as R1:
with zopen(file2, 'r') as R2:
请注意,您可以使其更加简洁,而不会降低可读性:
zopen = gzip.open if zipped else open
with zopen(file1, 'r') as R1, zopen(file2, 'r') as R2:
答案 1 :(得分:3)
您可以通过创建一个检查文件类型的函数来按原始方式进行操作。
def open_or_gzip_open(file_name, permissions='r'):
if file_name.endswith('gz'):
R1 = gzip.open(file_name, 'r')
else:
R1 = open(file_name, 'r')
return R1
您可以在一行上打开两个文件:
with open_or_gzip_open('text.txt') as p1, open_or_gzip_open('text2.txt') as p2:
print(p1, p2)
〜
答案 2 :(得分:2)
只要zipped
为False
,第二个代码就和第一个一样工作。
但是要注意的重要一件事是,您不必嵌套with语句。相反,您可以执行以下操作:
with open(file1) as R1, open(file2) as R2:
###code
(此外,函数open
默认情况下具有'r'
参数,因此您不必将其包括在内。)