分解with语句

时间:2018-07-11 19:55:36

标签: python

最初,我一直在打开并同时读取两个文件,如下所示:

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

第二个代码是否像第一个一样起作用?还是有更好的方法呢?

3 个答案:

答案 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.openopen都是可调用的对象,因此您可以将它们存储在变量中,然后再调用它。由于它们具有相同的签名,并且您希望使用完全相同的参数来调用它们,因此使用该变量很简单:

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)

只要zippedFalse,第二个代码就和第一个一样工作。

但是要注意的重要一件事是,您不必嵌套with语句。相反,您可以执行以下操作:

with open(file1) as R1, open(file2) as R2:
    ###code

(此外,函数open默认情况下具有'r'参数,因此您不必将其包括在内。)