用1行代码打开读取和关闭文件


128

现在我使用:

pageHeadSectionFile = open('pagehead.section.htm','r')
output = pageHeadSectionFile.read()
pageHeadSectionFile.close()

但是为了使代码看起来更好,我可以这样做:

output = open('pagehead.section.htm','r').read()

使用上述语法时,如何关闭文件以释放系统资源?


19
一线无内在的吸引力。读取代码的频率远远超过编写代码的频率,应该编写代码来理解,而不是为了“冷静”。唯一的例外是在一种语言中有一个众所周知的习惯用法,但在这种情况下我没有意识到。
drdwilcox 2011年

17
@drdwilcox:隐秘的一线是不好的,声明性的一线是好的。没有理由(至少我看不到),为什么在内核中没有函数包装器可以在单个函数调用中读取文件(这种常见需求)。有点像contents = os.readfile(path)。如果我想做些更奇特的事情,那好吧,我会很乐意使用with open(path) as fd: contents = fd.read()。当然,可以编写自己的包装程序,但这就是内核的用途,它为程序员提供了有用的抽象方法。
tokland

5
的确,对代码的读取远比对代码的读取要多,但是暗示长代码和短代码一样好,这是不对的。如果您花时间使代码尽可能短(而不必诉诸难以理解的聪明技巧),那么在读取代码时,这种投资将一次又一次地得到回报。您写的每一行对任何阅读您的代码的人都是不利的,因此您应尽量减少编写。记住帕斯卡(Pascal)的一句名言:“我把这封信加长只是因为我没有空余地把它缩短。”
约翰·威廉姆斯,

Answers:


195

您实际上不必关闭它-Python将在垃圾回收期间或程序退出时自动完成它。但是正如@delnan指出的,出于各种原因,最好将其显式关闭。

因此,可以做些什么来使其简短,简单和明确:

with open('pagehead.section.htm','r') as f:
    output = f.read()

我认为,现在只有两行并且可读性强。


2
@ 1qazxsw2如果使用该with语句,文件资源将为您正确关闭。
David Alber

13
第一句话:Python 最终将关闭它。但这并不意味着您应该忘记关闭。即使进行了重新计数,文件打开的时间也可能比您想像的要长得多(例如,如果碰巧被循环引用)。在具有不错的GC的Python实现中,这是三次,您不能保证在任何特定时间都对GC进行了处理。甚至CPython文档都说您不应该依赖GC进行此类清理。答案的后半部分应为粗体。

6
如果你真的需要一个班轮,有可能把output = f.read()部分后的同一行:
Karl Knechtel

1
“用1行代码打开读取和关闭文件”这是两行,并且没有回答问题。
user5359531

1
这取决于实现方式-请参阅Sven的答案。
蒂姆·皮茨克

71

Python标准库Pathlib模块可满足您的需求:

Path('pagehead.section.htm').read_text()

不要忘记导入路径:

jsk@dev1:~$ python3
Python 3.5.2 (default, Sep 10 2016, 08:21:44)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pathlib import Path
>>> (Path("/etc") / "hostname").read_text()
'dev1.example\n'

在Python 27上安装反向移植pathlibpathlib2


8
提出的其他答案with很好,但with只是陈述,而不是表达。该pathlib答案是对可以嵌入在Python表达式中的原始问题的唯一答复。像SECRET_KEY = os.environ.get('SECRET_KEY') or pathlib.Path('SECRET_KEY').read_bytes()
LeoRochael

24

使用CPython,将在执行该行后立即关闭您的文件,因为该文件对象会立即被垃圾回收。但是,有两个缺点:

  1. 在不同于CPython的Python实现中,通常不会立即关闭文件,而是在以后的时间,超出您的控制范围。

  2. 在Python 3.2或更高版本中ResourceWarning,如果启用,将抛出。

最好再投资一条线:

with open('pagehead.section.htm','r') as f:
    output = f.read()

这将确保在所有情况下都正确关闭文件。


17

无需导入任何特殊的库即可执行此操作。

使用常规语法,它将打开文件进行读取,然后将其关闭。

with open("/etc/hostname","r") as f: print f.read() 

要么

with open("/etc/hosts","r") as f: x = f.read().splitlines()

这将为您提供一个包含行的数组x,并且可以这样打印:

for line in x: print line

这些单行代码对于维护非常有帮助-基本上是自我记录。


8

您可以做的是使用该with语句,并在一行上写下两个步骤:

>>> with open('pagehead.section.htm', 'r') as fin: output = fin.read();
>>> print(output)
some content

with语句将谨慎地调用__exit__给定对象的函数,即使代码中发生了一些问题;它接近try... finally语法。对于由返回的对象open__exit__对应于文件关闭。

该语句已在Python 2.6中引入。


小澄清:根据文件 with在Python 2.5中引入的,但必须明确的进口__future__。它在Python 2.6中的所有上下文中均可用。
David Alber

5

使用ilio:(inline io):

仅一个函数调用,而不是文件open(),read(),close()。

from ilio import read

content = read('filename')

2
with open('pagehead.section.htm')as f:contents=f.read()

4
这与前三个答案有何不同?
所有工人都是必不可少的

4
最大的区别在于,它仅是指定问题中的一行。就我个人而言,我找不到其他任何东西,但是可以随意批评我的工作,而不是自己亲自提出问题。

3
在Python中实现打开,读取和关闭文件的最短的内置方法是使用2条逻辑行,无论该文件是否压缩为1行。因此,我认为此答案与3个原始答案没有任何实质性的区别。
所有工人都很重要

1
它的“有效”是否不同并不重要。我到此页面寻找可能python -c在命令行上使用的单行语法,因此发布两行答案无济于事。
user5359531

1
@ user5359531我看不出你的意思:你知道你可以用引号python表达式";用来附加两个指令,并在之后删除换行符:吗?下面的表达式工作得很好对我来说:$> python -c "with open('some file', 'r') as f: print(next(f))"
乔尔

2

我认为实现此目标的最自然的方法是定义一个功能。

def read(filename):
    f = open(filename, 'r')
    output = f.read()
    f.close()
    return output

然后,您可以执行以下操作:

output = read('pagehead.section.htm')

0

当我需要在日志文件中抓取一些内容时,我经常这样做:

$ grep -n "xlrd" requirements.txt | awk -F ":" '{print $1}'
54

$ python -c "with open('requirements.txt') as file: print ''.join(file.readlines()[52:55])"
wsgiref==0.1.2
xlrd==0.9.2
xlwt==0.7.5

1
完全无关最初的话题,但你应该看看grep -A <n>grep -B <n>grep -C <n>,如果它是有帮助的。更多信息:stackoverflow.com/a/9083/1830159
Liam Stanley

0

使用more_itertools.with_iter,可以output在一行中打开,读取,关闭和分配等效项(不包括import语句):

import more_itertools as mit


output = "".join(line for line in mit.with_iter(open("pagehead.section.htm", "r")))

虽然可能,但我会寻找另一种方法,而不是将文件的内容分配给变量,即延迟迭代-这可以使用传统with块来完成,也可以在上面的示例中通过删除join()和迭代来完成output


您也可以在oneliner中导入。"".join(line for line in __import__('more_itertools').with_iter(open("pagehead.section.htm", "r")))这样就可以正常工作,并且不需要导入行。
melwil '17

1
我完全同意你的观点。但是,在与oneliners讨论解决任务的过程中,我经常发现自己陷入争论,即议定结果应该是将一行代码粘贴到新的python shell中。这样的挑战很少符合pep8。绝不是编写代码的好习惯,它只是作为消除导入需求的提示。
melwil

0

如果你想要的温暖和模糊的感觉,只是去用

对于python 3.6,我在IDLE的新起点下运行了这两个程序,给出了以下运行时:

0.002000093460083008  Test A
0.0020003318786621094 Test B: with guaranteed close

所以没有太大的区别。

#--------*---------*---------*---------*---------*---------*---------*---------*
# Desc: Test A for reading a text file line-by-line into a list
#--------*---------*---------*---------*---------*---------*---------*---------*

import sys
import time

#                                  # MAINLINE
if __name__ == '__main__':
    print("OK, starting program...")

    inTextFile = '/Users/Mike/Desktop/garbage.txt'

#                                  # Test: A: no 'with;
    c=[]
    start_time = time.time()
    c = open(inTextFile).read().splitlines()
    print("--- %s seconds ---" % (time.time() - start_time))

    print("OK, program execution has ended.")
    sys.exit()                     # END MAINLINE

输出:

OK, starting program...
--- 0.002000093460083008 seconds ---
OK, program execution has ended.

#--------*---------*---------*---------*---------*---------*---------*---------*
# Desc: Test B for reading a text file line-by-line into a list
#--------*---------*---------*---------*---------*---------*---------*---------*

import sys
import time

#                                  # MAINLINE
if __name__ == '__main__':
    print("OK, starting program...")

    inTextFile = '/Users/Mike/Desktop/garbage.txt'

#                                  # Test: B: using 'with'
    c=[]
    start_time = time.time()
    with open(inTextFile) as D: c = D.read().splitlines()
    print("--- %s seconds ---" % (time.time() - start_time))

    print("OK, program execution has ended.")
    sys.exit()                     # END MAINLINE

输出:

OK, starting program...
--- 0.0020003318786621094 seconds ---
OK, program execution has ended.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.